1 Introducción

2 Objetivos

El análisis y los modelos que se pretenden construir sólo abarcan el área urbana de Medellín correspondiente a sus 16 comunas y sus respectivos barrios.

2.1 General

Construir un sistema que le permita a la Secretaría de Movilidad de Medellín tomar decisiones en la creación de estrategias que ayuden a reducir la accidentalidad en zona específicas de Medellín, observando cuales son los tipos de accidentes más frecuentes en las diferentes comunas y sus barrios.

También se busca que el sistema les permita a todos los ciudadanos que se movilizan en un vehículo tomar precauciones respecto a los accidentes más frecuentes que suceden en lugares específicos.

Lo anterior creando una interfaz amigable para los usuarios y fácil de utilizar.

2.2 Específicos

  • Construir modelos que predigan el número de accidentes de cada tipo, tomando como entradas una ventana y una resolución temporal específicas, además de una zona espacial que puede ser un barrio o comuna de Medellín.

  • Elaborar un sistema que agrupe barrios con características similares por los tipos de accidentes (clase) y que tienen lugar en estos.

  • Crear una interfaz gráfica que le permita al usuario visualizar con mayor comodidad la accidentalidad que hay en Medellín y las predicciones de esta a futuro.

3 Descripción y Limpieza de los datos

Los datos utilizados se encuentran en las bases de datos de Geomedellín (Portal Geográfico del Municipio de Medellín) y contienen información referente a múltiples accidentes de tránsito, en los que se detalla el tipo de accidente, dónde y cuándo ocurrió. La base de datos utilizada en este trabajo es una unión de las bases de incidentes en 2014, 2015, 2016, 2017 y 2018, se usará este último año para validar los modelos planteados.

La base de datos original cuenta con las siguientes variables:

  • X: Componente de coordenada.
  • Y: Componente de coordenada.
  • OBJECTID: Id de cada incidente.
  • RADICADO: Código emitido por la secretaría de movilidad de Medellín.
  • FECHA: Fecha del incidente.
  • HORA: Hora del incidente.
  • DIA: Día del mes en el que ocurre el incidente
  • PERIODO: Año del incidente.
  • CLASE: Tipo de accidente
  • DIRECCION.
  • DIRECCION_ENC.
  • CBML: Código de ubicación del predio en la ciudad.
  • TIPO_GEOCOD: Tipo de
  • GRAVEDAD: repercusiones del accidente
  • BARRIO.
  • COMUNA.
  • DISENO: clasificación del lugar del accidente.
  • DIA_NOMBRE: Día de la semana en el que ocurre el incidente
  • MES: Mes del incidente en número (del 1 al 12)
  • MES_NOMBRE: Mes del incidente
  • X_MAGNAMED: Componente de coordenada
  • Y_MAGNAMED: Componente de coordenada
  • LONGITUD: Componente de coordenada
  • LATITUD: Componente de coordenada

Se deciden eliminar las variables “X”, “Y”, “X_MAGNAMED” y “Y_MAGNAMED” ya que cumplen la misma función que las variables “LONGITUD” y “LATITUD”. También se elimina la variable “RADICADO” ya que sirve para identificar a un respectivo incidente, al igual que la variable “OBJECTID”. Se elimina la variable “MES_NOMBRE” ya que es redundante el la base de datos, debido a que se encuentra la misma información en la variable “MES”. Además ponerle los nombres a partir de la variable “MES” traería un aumento al coste computacional innecesario. También se decide eliminar las variables “DIRECCION”, “DIRECCION_ENC”, “CBML”, “TIPO_GEOCOD” y “HORA” ya no serán de utilidad en el modelo.

Además se crea otra variable llamada “DIA_FESTIVO”. Para poder analizar el número de accidentes en los días festivos y demás fechas especiales. Por lo tanto, las variables de interés que se usan en este proyecto son las siguientes:

  • OBJECTID: Id de cada incidente.
  • CLASE: Tipo de accidente
  • GRAVEDAD: repercusiones del accidente
  • COMUNA.
  • BARRIO.
  • DISENO: clasificación del lugar del accidente.
  • LATITUD: Componente de coordenada
  • LONGITUD: Componente de coordenada
  • FECHA: Fecha del incidente.
  • DIA: Día del mes en el que ocurre el incidente
  • MES: Mes del incidente en número (del 1 al 12)
  • PERIODO: Año del incidente.
  • DIA_NOMBRE: Día de la semana en el que ocurre el incidente
  • DIA_FESTIVO.

Definido lo anterior, se realiza una limpieza en la base de datos, ya que hay registros u observaciones que están mal escritos o simplemente no deberían estar. A continuación se explican los cambios realizados.

3.1 Depuración

Se decide quitar los acentos a cada una de las variables categóricas debido a que hay valores repetidos los cuales representan el mismo nivel o categoria, pero con acento y sin acento. Un ejemplo de esto es el barrio Berlin de la comuna Aranjuez, el cual aparece 647 veces con el nombre de “Berlin” y 15 veces con el nombre de “Berlín”. Por lo tanto se decide arreglar este problema.

3.1.1 Para COMUNA

La variable COMUNA debe tener las comunas urbanas de Medellín y sus corregimientos.

Se sabe que en Medellín hay 16 comunas urbanas y 5 corregimientos, ambos están compuestos por barrios, por lo que la variable debería tener 16 + 5 = 21 niveles. Pero al observar los niveles de la variable “COMUNA” en el conjunto de entrenamiento, se evidencian 84 comunas, por lo cual se decide buscar las razones de esto y tratar de corregirlo.

Comunas: Popular, Santa Cruz, Manrique, Aranjuez, Castilla, Doce de Octubre, Robledo, Villa Hermosa, Buenos Aires, La Candelaria, Laureles-Estadio, La América, San Javier, El Poblado, Guayabal, Belén.

Corregimientos: Corregimiento de San Cristóbal, Corregimiento de San Antonio de Prado, Corregimiento de Santa Elena, Corregimiento de Altavista, Corregimiento de San Sebastián de Palmitas.

Uno de los problemas que se encuentra es que la persona encargada de digitar los datos se confundió entre las variables “COMUNA” y “BARRIO”. Un ejemplo de esto es el barrio Boston que aparece como comuna en 2 observaciones, con su respectiva comuna (La candelaria) en barrio. Así como el ejemplo anterior, hay varios casos. Por lo cual, estas comunas y barrios se ponen en su posición correcta.

Luego de resolver el problema anterior, se encuentra que aún hay comuna que no pertenecen a las 21 mencionadas anteriormente, estas son: “In”, “AU”, “SN”, “0”. Al no encontrar información de estos valores en la página oficial o en internet, se decide reemplazar estos valores con sus respectivos barrios como datos faltantes o NA.

3.1.2 Para BARRIO

Se detecta que los barrios contienen algunos problemas de espacios, algunos ejemplos son:

  • “Asomadera No. 1” y “Asomadera No.1”
  • “Aures No. 2” y “Aures No.2”
  • “Bombona No. 1” y “Bombona No.1”
  • “B. Cerro El Volador” y “B. Cerro El Volador”

Por lo tanto se arregla este problema quitando el espacio que hay entre “No.” y su número correspondiente en los barrios que tienen esta característica. También se eliminan los espacios que hay antes de la primera letra y después de la última letra.

También se reemplaza “Barrios de Jesús” por “Barrio de Jesús”, “Nueva Villa de La Iguana” por “Nueva Villa de la Iguana”, “Santa María de Los Ángeles” por “Santa María de los Ángeles”, “Villa Lilliam” por “Villa Liliam”.

3.1.3 Para CLASE

“Caida de Ocupante” se reemplaza por “Caida Ocupante” ya que se consideran equivalentes. También se elimina la observación con CLASE = “Choque y Atropello” ya que solo hay una.

3.1.4 Para DIA_NOMBRE

Se eliminan los espacios que hay antes de la primera letra y después de la última letra en todas las observaciones.

3.2 Imputación

En los datos faltantes de la variable DISENO, se decide usar el nombre de “No Registrado” para reemplazarlos.

En la base de datos se tiene la siguiente cantidad de datos faltantes por variable: la variable BARRIO tiene 19957 de datos faltantes, COMUNA tiene 19957 y CLASE tiene 7. Las demás variables no contienen datos faltantes.

Como la variable CLASE solo tiene 7 datos faltantes, se decide eliminar estas observaciones. Por el contrario, como BARRIO y COMUNA presentan una cantidad importante de los datos, se decide realizar una imputación de los mismos.

3.2.1 Para COMUNA

Primero se decide realizar la imputación en la variable COMUNA, se utiliza un modelo knn (k-Nearest Neighbour) con variable respuesta COMUNA y como predictoras las variables LATITUD y LONGITUD escaladas. Dado que este es un modelo que usa la distancia euclidiana para clasificar observaciones, se piensa que funcionaría bien para clasificar a las comunas teniendo su ubicación en latitud y longitud.

Para ajustar el modelo se usó la función train del paquete caret, usando K-Fold CV con K=10 y el número de vecinos igual a 1, 4, 7, 10, 13, 16 y 19. Se entrenó el modelo usando los años de 2014 a 2017 y se validó con el año 2018. Se usó la precisión de prueba del modelo como función de optimización. Los resultados obtenidos son los siguientes:

Accuracy Kappa AccuracyLower AccuracyUpper AccuracyNull AccuracyPValue
0.9985 0.9984 0.9981 0.9989 0.2062 0

Los resultados se consideran buenos y se decide realizar el reemplazo de los valores faltantes de COMUNA por medio de este modelo.

3.2.2 Para BARRIO

Se decide usar un modelo knn (k-Nearest Neighbour) con variable respuesta BARRIO y como predictoras las variables LATITUD y LONGITUD escaladas y COMUNA.

Dado que habían valores de barrios que están en el año 2018 pero no en los demás, no se pudo hacer uso del paquete caret ya que presentaba un error al entrenar el modelo, por lo que se decide usar la función knn.cv del paquete class, para medir el comportamiento del modelo knn con k = 1 mediante LOOCV, entrenando el modelo con los años de 2014 hasta 2017 y validando el resultado con el año 2018. Se obtiene una precisión de prueba de 0.9975, por lo que se decide usar este modelo para imputar los datos faltantes en BARRIO.

3.3 Base final

Finalmente se decide usar solo las comunas del área urbana de Medellín y dejar a un lado los corregimientos, es decir, solo se tendrán las 16 comunas mencionadas anteriormente en este reporte.

Tras las modificaciones mencionadas, de las 228693 observaciones originales entre las bases de datos del 2014 al 2018, se utilizarán 204002 observaciones en el análisis y construcción de los modelos. La base de datos luce de esta manera:

df <- read.csv(file = "Base_definitiva.csv", header = T, nrows = 10000,
               stringsAsFactors = T)
df$FECHA <- as.Date(df$FECHA)
df

A continuación se encuentra el acceso al código utilizado en todo este proceso

Enlace: https://github.com/vagarciave/Project_x/blob/master/limpieza/Depuracion.Rmd

4 Análisis descriptivo

5 Modelos Predictivos

Se define la accidentalidad como el número de accidentes que hay a nivel diario, semanal y mensual, discriminando por tipo de accidente (CLASE) y barrio o comuna donde éste ocurre. Lo anterior debido a que existe una variabilidad notable a nivel de accidentalidad para cada columna y para cada barrio como se observó en el análisis descriptivo.

Dado que el objetivo es predecir la accidentalidad a nivel diario, mensual o semanal, se busca construir modelos que predigan el número de accidentes en cada uno de los rangos temporales es decir se crearán modelos específicos para cada comuna/barrio según la unidad temporal.

Se piensa que los modelos mixtos o modelos jerárquicos pueden representar bien esta definición de accidentalidad, debido a la agrupación que existe al momento de definir la accidentalidad, es decir, existe una agrupación entre comuna y clase; y otra agrupación entre barrio y clase. Por lo que se decide crear estos modelos con la ayuda del paquete lme4 y medir su ajuste. En cada uno de estos casos se crea un modelo que prediga el número de accidentes, por lo que se considera que la accidentalidad puede seguir una distribución Poisson.

Para evaluar los modelos se crean conjuntos de entrenamiento y conjuntos de validación. Los conjuntos de entrenamiento constan de todas las combinaciones posibles de tipos de accidentes según si es comuna o barrio y su unidad de tiempo con los años (periodos) del 2014 al 2017, el conjunto de validación será con el año 2018.

La medida para evaluar el ajuste de los modelos será el error cuadrático medio en las predicciones tanto para los conjuntos de entrenamiento como para los de validación.

\[MSE = \frac{\sum_{i=1}^N (accidentalidad_i - \widehat{accidentalidad_i)^2}}{N}\]

Con la ayuda de R se crea la siguiente función para obtenerlo:

MSE <- function(y, y_est) mean((y-y_est)**2)

Se usarán los siguientes paquetes para ajustar los modelos

library(lme4)      # Paquete para la creación de modelos mixtos poisson
library(tidyverse) # Paquete para la creación de los conjuntos de datos

5.1 Ajuste de la base de datos

Se debe modificar la base de datos depurada para poder realizar los modelos. Se crean variables de tiempo a nivel diario, semanal y mensual, además de la variable accidentalidad y días festivos.

#  Vector con fechas de dias festivos
  # Definir dias festivos
  festivos <- ymd(c(
    #2014
    '2014-01-01','2014-01-06','2014-03-24','2014-04-17','2014-04-18', '2014-05-01',
    '2014-06-02','2014-06-23','2014-06-30','2014-07-20','2014-08-07', '2014-08-18',
    '2014-10-13','2014-11-03','2014-11-17','2014-12-08','2014-12-25',
    #2015
    '2015-01-01','2015-01-12','2015-03-23','2015-03-29','2015-04-02','2015-04-03',
    '2015-04-05','2015-05-01','2015-05-18','2015-06-08','2015-06-15','2015-06-29',
    '2015-07-20','2015-08-07', '2015-08-17','2015-10-12','2015-11-02','2015-11-16',
    '2015-12-08','2015-12-25',
    #2016
    '2016-01-01','2016-01-11','2016-03-20','2016-03-21','2016-03-24','2016-03-25',
    '2016-03-27','2016-05-01','2016-05-09','2016-05-30','2016-06-06','2016-07-04',
    '2016-07-20','2016-08-07', '2016-08-15','2016-10-17','2016-11-07','2016-11-14',
    '2016-12-08','2016-12-25',
    #2017
    '2017-01-01','2017-01-09','2017-03-20','2017-04-09','2017-04-13','2017-04-14',
    '2017-04-16','2017-05-01','2017-05-29','2017-06-19','2017-06-26','2017-07-03',
    '2017-07-20','2017-08-07', '2017-08-21','2017-10-16','2017-11-06','2017-11-13',
    '2017-12-08','2017-12-25',
    #2018
    '2018-01-01','2018-01-08','2018-03-19','2018-03-25','2018-03-29','2018-03-30',
    '2018-04-01','2018-05-01','2018-05-14','2018-06-04','2018-06-11','2018-07-02',
    '2018-07-20','2018-08-07', '2018-08-20','2018-10-15','2018-11-05','2018-11-11',
    '2018-12-08','2018-12-25',
    #2019
    '2019-01-01','2019-01-07','2019-03-25','2019-04-18','2019-04-19','2019-05-1',
    '2019-06-03','2019-06-24','2019-07-01','2019-07-20','2019-08-07','2019-08-19',
    '2019-10-14','2019-11-04','2019-11-11','2019-12-08','2019-12-25',
    #2020
    '2020-01-01','2020-01-06','2020-03-23','2020-04-9','2020-04-10','2020-05-01',
    '2020-05-25','2020-06-15','2020-06-22','2020-06-29','2020-07-20','2020-08-07',
    '2020-08-17','2020-10-12','2020-11-02','2020-11-16','2020-12-08','2020-12-25'
    ))

df <- df %>% select(COMUNA, CLASE, FECHA, PERIODO, MES, DIA_NOMBRE)

df$FECHA <- as.Date(df$FECHA)

# Se agrega la variable TIEMPO y MES_NOMBRE
df$MES_NOMBRE <- paste(df$PERIODO, df$MES, sep="-") %>% as.yearmon("%Y-%m")

# Para obtener la inversa se usaría: zoo::as.Date(df$FECHA, origin="2014-01-01")
df$TIEMPO_DIA <- as.numeric(as.Date(df$FECHA)) - as.numeric(as.Date("2014-01-01")) + 1

# Se crea la variable SEMANA
df <- df %>% mutate(SEMANA = strftime(FECHA, format = "%Y-%V"),
                          TIEMPO_SEMANA = match(SEMANA, sort(unique(SEMANA))))

# Se crea la variable MES
df <- df %>% mutate(MES = strftime(FECHA, format = "%Y-%m"),
                          TIEMPO_MES = match(MES, sort(unique(MES))))

# días festivos
df <- df %>% mutate(DIA_FESTIVO = ifelse(ymd(FECHA) %in% festivos,1,0))

# accidentalidad
df <- df %>% mutate(ACCIDENTALIDAD = 1)

5.2 Modelos predictivos para las Comunas

Para obtener la base datos en la cual se encuentre el número de accidentes para cada evento considerado, se crea una base de datos con todos los eventos posibles con ayuda de la función expand.grid, en la cual se tiene en cuenta todas la combinaciones de comuna, clase (tipo de accidente) y fecha.

fecha_vector <- as.Date(as.Date("2014-01-01"):as.Date("2018-12-31"))
base <- expand.grid(COMUNA = levels(df$COMUNA), CLASE = levels(df$CLASE),
                             FECHA = fecha_vector)
base <- base %>% mutate(TIEMPO_DIA = as.numeric(FECHA) -
                                            as.numeric(as.Date("2014-01-01")) + 1)

# PERIODO
base <- base %>% mutate(PERIODO = as.numeric(format(FECHA,'%Y')))

# Se crea la variable SEMANA
base <- base %>% mutate(SEMANA = strftime(FECHA, format = "%Y-%V"),
                          TIEMPO_SEMANA = match(SEMANA, sort(unique(SEMANA))))

# Se crea la variable MES
base <- base %>% mutate(MES = strftime(FECHA, format = "%Y-%m"),
                          TIEMPO_MES = match(MES, sort(unique(MES))))

# días festivos
base <- base %>% mutate(DIA_FESTIVO = ifelse(ymd(FECHA) %in% festivos,1,0))

Luefo se realiza un left join con la base que contiene todos los posibles eventos y la base de datos que contiene la información de los accidentes (base depurada).

base <- left_join(base, subset(df, select = -DIA_NOMBRE),
                  by = c("COMUNA", "CLASE", "FECHA", "TIEMPO_DIA",
                                       "PERIODO", "SEMANA", "TIEMPO_SEMANA",
                                       "TIEMPO_MES", "DIA_FESTIVO"))
base[is.na(base)] <- 0

accidentes_dia_comuna <- left_join(base, distinct(df[, c("FECHA", "DIA_NOMBRE")]), by = "FECHA")

5.2.1 Modelos - unidad de tiempo Día

La base de datos con el número de accidentes por comuna, clase y día se obtuvo anteriormente, ésta contiene 175296 observaciones.

dim(accidentes_dia_comuna)
## [1] 175296     12

La base luce de la siguiente manera:

Se realiza la partición de los datos para seleccionar el conjunto de entrenamiento y el conjunto de prueba.

test_dia_comuna <- accidentes_dia_comuna[accidentes_dia_comuna$PERIODO == 2018, ]
train_dia_comuna <- accidentes_dia_comuna[accidentes_dia_comuna$PERIODO %in%
                                            c(2014, 2015, 2016, 2017), ]

Se procede a la creación de modelos:

Se consideran distintos modelos partiendo de un modelo jerárquico con 2 niveles (CLASE y COMUNA) con solo un intercepto, ambos niveles o variables con efectos aleatorios se consideran correlacionados y se pondrá la variable CLASE (tipo de accidente) dentro de la variable comuna.

mod_dia_comuna0 <- glmer(ACCIDENTALIDAD ~ 1 + (1 | COMUNA/CLASE),
                         data = train_dia_comuna, family= poisson())
mod_dia_comuna1 <- glmer(ACCIDENTALIDAD ~ DIA_FESTIVO + (1 | COMUNA/CLASE),
                         data = train_dia_comuna, family= poisson())
mod_dia_comuna2 <- glmer(ACCIDENTALIDAD ~ DIA_FESTIVO + TIEMPO_DIA + (1 | COMUNA/CLASE),
                         data = train_dia_comuna, family= poisson())
mod_dia_comuna3 <- glmer(ACCIDENTALIDAD ~ DIA_FESTIVO + DIA_NOMBRE + (1 | COMUNA/CLASE),
                         data = train_dia_comuna, family= poisson())
mod_dia_comuna4 <- glmer(ACCIDENTALIDAD ~ DIA_FESTIVO + DIA_NOMBRE +
                           (1 + DIA_FESTIVO| COMUNA/CLASE),
                         data = train_dia_comuna, family= poisson())

Se comparan los modelos por un analísis de varianza ANOVA:

anova(mod_dia_comuna0, mod_dia_comuna1, mod_dia_comuna2, mod_dia_comuna3, mod_dia_comuna4)

Al parecer cada modelo explica mejor la accidentalidad que el anterior, pero se debe recordar que el Valor-P es sensible al número de datos. En la base de entrenamiento hay 140256 observaciones, esto se considera grande, por lo que no hay que fijarse mucho en el Valor-P dado que es más fácil que rechace las hipótesis nulas.

Se procede a calcular el MSE de entrenamiento y de prueba en cada uno de los modelos, cabe resaltar que los valores predichos se redondearán al entero más cercano debido a que son datos de conteo y la función predict devuelve valores en la escala original pero con décimales.

# Modelo 0
y_est_train_dia_comuna0 <- round(predict(mod_dia_comuna0, newdata = train_dia_comuna,
                                         type = "response"),0)
y_est_test_dia_comuna0 <- round(predict(mod_dia_comuna0, newdata = test_dia_comuna,
                                        type = "response"),0)
mse_train_dia_comuna0 <- round(MSE(train_dia_comuna$ACCIDENTALIDAD,
                                   y_est_train_dia_comuna0),4)
mse_test_dia_comuna0 <- round(MSE(test_dia_comuna$ACCIDENTALIDAD,
                                 y_est_test_dia_comuna0),4)

# Modelo 1
y_est_train_dia_comuna1 <- round(predict(mod_dia_comuna1, newdata = train_dia_comuna,
                                         type = "response"),0)
y_est_test_dia_comuna1 <- round(predict(mod_dia_comuna1, newdata = test_dia_comuna,
                                        type = "response"),0)
mse_train_dia_comuna1 <- round(MSE(train_dia_comuna$ACCIDENTALIDAD,
                                   y_est_train_dia_comuna1),4)
mse_test_dia_comuna1 <- round(MSE(test_dia_comuna$ACCIDENTALIDAD,
                                  y_est_test_dia_comuna1),4)

# Modelo 2
y_est_train_dia_comuna2 <- round(predict(mod_dia_comuna2, newdata = train_dia_comuna,
                                         type = "response"),0)
y_est_test_dia_comuna2 <- round(predict(mod_dia_comuna2, newdata = test_dia_comuna,
                                        type = "response"),0)
mse_train_dia_comuna2 <- round(MSE(train_dia_comuna$ACCIDENTALIDAD,
                                   y_est_train_dia_comuna2),4)
mse_test_dia_comuna2 <- round(MSE(test_dia_comuna$ACCIDENTALIDAD,
                                  y_est_test_dia_comuna2),4)

# Modelo 3
y_est_train_dia_comuna3 <- round(predict(mod_dia_comuna3, newdata = train_dia_comuna,
                                         type = "response"),0)
y_est_test_dia_comuna3 <- round(predict(mod_dia_comuna3, newdata = test_dia_comuna,
                                        type = "response"),0)
mse_train_dia_comuna3 <- round(MSE(train_dia_comuna$ACCIDENTALIDAD,
                                   y_est_train_dia_comuna3),4)
mse_test_dia_comuna3 <- round(MSE(test_dia_comuna$ACCIDENTALIDAD,
                                  y_est_test_dia_comuna3),4)

# Modelo 4
y_est_train_dia_comuna4 <- round(predict(mod_dia_comuna4, newdata = train_dia_comuna,
                                         type = "response"),0)
y_est_test_dia_comuna4 <- round(predict(mod_dia_comuna4, newdata = test_dia_comuna,
                                        type = "response"),0)
mse_train_dia_comuna4 <- round(MSE(train_dia_comuna$ACCIDENTALIDAD,
                                   y_est_train_dia_comuna4),4)
mse_test_dia_comuna4 <- round(MSE(test_dia_comuna$ACCIDENTALIDAD,
                                  y_est_test_dia_comuna4),4)

Modelo_Mixto_Poisson Train_MSE Test_MSE Porcentaje_Variacion
ACCIDENTALIDAD ~ 1 + (1 | COMUNA/CLASE) 2.0222 1.9352 2.20
ACCIDENTALIDAD ~ DIA_FESTIVO + (1 | COMUNA/CLASE) 1.9038 1.8241 2.14
ACCIDENTALIDAD ~ DIA_FESTIVO + TIEMPO_DIA + (1 | COMUNA/CLASE) 1.9010 1.8598 1.10
ACCIDENTALIDAD ~ DIA_FESTIVO + DIA_NOMBRE + (1 | COMUNA/CLASE) 1.5791 1.5311 1.54
ACCIDENTALIDAD ~ DIA_FESTIVO + DIA_NOMBRE + (1 + DIA_FESTIVO | COMUNA/CLASE) 1.5400 1.5118 0.92

En los modelos mod_dia_comuna3 y mod_dia_comuna4 se descarta usar la variable TIEMPO_DIA debido a que en lugar en disminuir el Test_MSE, lo aumentó, y tampoco mejoró el Train_MSE significativamente, por lo que se considera que no es significativa para explicar la accidentalidad en las comunas de Medellín por tipo de accidente.

Finalmente se observa que el modelo con intercepto y pendiente aleatoria mod_dia_comuna4:

\[\widehat{\text{ACCIDENTALIDAD}} = \text{DIA_FESTIVO} + \text{DIA_NOMBRE} + (1 + \text{DIA_FESTIVO} | \text{COMUNA/CLASE})\]

Es el que obtiene el menor Train_MSE y Test_MSE, por lo que se escoge este modelo para la explicación de la accidentalidad en comuna por día.

A continuación se muestran algunos de los valores observados de la accidentalidad diaria por clase y por comuna, con su respectivo valor predicho última columna) en el conjunto de prueba.

5.2.2 Modelos - unidad de tiempo Semana

Para este modelo se agrupa la unidad de tiempo por semanas, en las cuales se suma la cantidad de accidentes que suceden el la misma, también se suma el número de días festivos. La base luce como sigue:

accidentes_semana_comuna <- accidentes_dia_comuna %>%
  group_by(COMUNA, CLASE, PERIODO, SEMANA, TIEMPO_SEMANA) %>%
  summarise(DIA_FESTIVO = sum(DIA_FESTIVO), ACCIDENTALIDAD = sum(ACCIDENTALIDAD))

accidentes_semana_comuna

Se procede a realizar la partición del conjunto de entrenamiento y el conjunto de prueba:

test_semana_comuna <- accidentes_semana_comuna[accidentes_semana_comuna$PERIODO == 2018, ]
train_semana_comuna <- accidentes_semana_comuna[accidentes_semana_comuna$PERIODO %in%
                                                  c(2014, 2015, 2016, 2017), ]

Ahora se ajustan los modelos mixtos con efectos aleatorios en COMUNA y CLASE al igual que se hizo a nivel diario, es decir, CLASE dentro de COMUNA.

mod_semana_comuna0 <- glmer(ACCIDENTALIDAD ~  1 + (1 | COMUNA/CLASE),
               data = train_semana_comuna, family= poisson())
mod_semana_comuna1 <- glmer(ACCIDENTALIDAD ~  DIA_FESTIVO + (1 | COMUNA/CLASE),
               data = train_semana_comuna, family= poisson())
mod_semana_comuna2 <- glmer(ACCIDENTALIDAD ~  DIA_FESTIVO + TIEMPO_SEMANA +
                              (1 | COMUNA/CLASE),
               data = train_semana_comuna, family= poisson())
mod_semana_comuna3 <- glmer(ACCIDENTALIDAD ~  DIA_FESTIVO +
                              (1 + DIA_FESTIVO | COMUNA/CLASE),
               data = train_semana_comuna, family= poisson())

Se comparan los modelos por un analísis de varianza ANOVA:

anova(mod_semana_comuna0, mod_semana_comuna1, mod_semana_comuna2, mod_semana_comuna3)

Se tiene el mismo problema del tamaño de muestra que en el modelo diario. Ahora, se procede a calcular el MSE de entrenamiento y de prueba en cada uno de los modelos, cabe resaltar que los valores predichos se redondearán al entero más cercano debido a que son datos de conteo y la función predict devuelve valores en la escala original pero con décimales.

# Modelo 0
y_est_train_semana_comuna0 <- round(predict(mod_semana_comuna0, newdata = train_semana_comuna,
                                         type = "response"),0)
y_est_test_semana_comuna0 <- round(predict(mod_semana_comuna0, newdata = test_semana_comuna,
                                        type = "response"),0)
mse_train_semana_comuna0 <- round(MSE(train_semana_comuna$ACCIDENTALIDAD,
                                   y_est_train_semana_comuna0),4)
mse_test_semana_comuna0 <- round(MSE(test_semana_comuna$ACCIDENTALIDAD,
                                 y_est_test_semana_comuna0),4)

# Modelo 1
y_est_train_semana_comuna1 <- round(predict(mod_semana_comuna1, newdata = train_semana_comuna,
                                         type = "response"),0)
y_est_test_semana_comuna1 <- round(predict(mod_semana_comuna1, newdata = test_semana_comuna,
                                        type = "response"),0)
mse_train_semana_comuna1 <- round(MSE(train_semana_comuna$ACCIDENTALIDAD,
                                   y_est_train_semana_comuna1),4)
mse_test_semana_comuna1 <- round(MSE(test_semana_comuna$ACCIDENTALIDAD,
                                  y_est_test_semana_comuna1),4)

# Modelo 2
y_est_train_semana_comuna2 <- round(predict(mod_semana_comuna2, newdata = train_semana_comuna,
                                         type = "response"),0)
y_est_test_semana_comuna2 <- round(predict(mod_semana_comuna2, newdata = test_semana_comuna,
                                        type = "response"),0)
mse_train_semana_comuna2 <- round(MSE(train_semana_comuna$ACCIDENTALIDAD,
                                   y_est_train_semana_comuna2),4)
mse_test_semana_comuna2 <- round(MSE(test_semana_comuna$ACCIDENTALIDAD,
                                  y_est_test_semana_comuna2),4)

# Modelo 3
y_est_train_semana_comuna3 <- round(predict(mod_semana_comuna3, newdata = train_semana_comuna,
                                         type = "response"),0)
y_est_test_semana_comuna3 <- round(predict(mod_semana_comuna3, newdata = test_semana_comuna,
                                        type = "response"),0)
mse_train_semana_comuna3 <- round(MSE(train_semana_comuna$ACCIDENTALIDAD,
                                   y_est_train_semana_comuna3),4)
mse_test_semana_comuna3 <- round(MSE(test_semana_comuna$ACCIDENTALIDAD,
                                  y_est_test_semana_comuna3),4)

Modelo_Mixto_Poisson Train_MSE Test_MSE Porcentaje_Variacion
ACCIDENTALIDAD ~ 1 + (1 | COMUNA/CLASE) 16.7066 15.1418 4.91
ACCIDENTALIDAD ~ DIA_FESTIVO + (1 | COMUNA/CLASE) 15.0649 13.2131 6.55
ACCIDENTALIDAD ~ DIA_FESTIVO + TIEMPO_SEMANA + (1 | COMUNA/CLASE) 14.9353 13.7151 4.26
ACCIDENTALIDAD ~ DIA_FESTIVO + (1 + DIA_FESTIVO | COMUNA/CLASE) 14.9111 13.1024 6.46

En el modelo mod_semana_comuna3 se descarta usar la variable TIEMPO_SEMANA debido a que en lugar en disminuir el Test_MSE, lo aumentó, y tampoco mejoró el Train_MSE significativamente, por lo que se considera que no es significativa para explicar la accidentalidad en las comunas de Medellín por tipo de accidente.

Finalmente se observa que el modelo con intercepto y pendiente aleatoria mod_semana_comuna3:

\[\widehat{\text{ACCIDENTALIDAD}} = \text{DIA_FESTIVO} + (1 + \text{DIA_FESTIVO} | \text{COMUNA/CLASE})\]

Es el que obtiene el menor Train_MSE y Test_MSE, por lo que se escoge este modelo para la explicación de la accidentalidad en comuna por semana.

A continuación se muestran algunos de los valores observados de la accidentalidad semanal por clase y por comuna, con su respectivo valor predicho (última columna) en el conjunto de prueba.

5.2.3 Modelos - unidad de tiempo Mes

Para este modelo se agrupa la unidad de tiempo por meses, en los cuales se suma la cantidad de accidentes que suceden el el mismo, también se suma el número de días festivos que hay en dicho mes. La base luce como sigue:

accidentes_mes_comuna <- accidentes_dia_comuna %>% group_by(COMUNA, CLASE, PERIODO, MES, TIEMPO_MES) %>%
  summarise(DIA_FESTIVO = sum(DIA_FESTIVO), ACCIDENTALIDAD = sum(ACCIDENTALIDAD))

accidentes_mes_comuna

Se realiza la partición de la base de entrenamiento y de prueba

test_mes_comuna <- accidentes_mes_comuna[accidentes_mes_comuna$PERIODO == 2018, ]
train_mes_comuna <- accidentes_mes_comuna[accidentes_mes_comuna$PERIODO %in%
                                            c(2014, 2015, 2016, 2017), ]

Ahora se ajustan los modelos mixtos con efectos aleatorios en COMUNA y CLASE al igual que se hizo a nivel diario y semanal, es decir, CLASE dentro de COMUNA.

mod_mes_comuna0 <- glmer(ACCIDENTALIDAD ~  1 + (1 | COMUNA/CLASE),
               data = train_mes_comuna, family= poisson())
mod_mes_comuna1 <- glmer(ACCIDENTALIDAD ~  DIA_FESTIVO + (1 | COMUNA/CLASE),
               data = train_mes_comuna, family= poisson())
mod_mes_comuna2 <- glmer(ACCIDENTALIDAD ~  DIA_FESTIVO + (1 + DIA_FESTIVO | COMUNA/CLASE),
               data = train_mes_comuna, family= poisson())

Analísis de varianza ANOVA

anova(mod_mes_comuna0, mod_mes_comuna1, mod_mes_comuna2)

Se observa que el modelo mod_mes_comuna2, el cual tiene intercepto y pendiente aleatoria con la característica DIA_FESTIVO no aporta significativamente a la explicación de la accidentalidad según el análisis de varianza ANOVA. Sin embargo, se procede a calcular los MSE de entrenamiento y de prueba.

# Modelo 0
y_est_train_mes_comuna0 <- round(predict(mod_mes_comuna0, newdata = train_mes_comuna,
                                         type = "response"),0)
y_est_test_mes_comuna0 <- round(predict(mod_mes_comuna0, newdata = test_mes_comuna,
                                        type = "response"),0)
mse_train_mes_comuna0 <- round(MSE(train_mes_comuna$ACCIDENTALIDAD,
                                   y_est_train_mes_comuna0),4)
mse_test_mes_comuna0 <- round(MSE(test_mes_comuna$ACCIDENTALIDAD,
                                 y_est_test_mes_comuna0),4)

# Modelo 1
y_est_train_mes_comuna1 <- round(predict(mod_mes_comuna1, newdata = train_mes_comuna,
                                         type = "response"),0)
y_est_test_mes_comuna1 <- round(predict(mod_mes_comuna1, newdata = test_mes_comuna,
                                        type = "response"),0)
mse_train_mes_comuna1 <- round(MSE(train_mes_comuna$ACCIDENTALIDAD,
                                   y_est_train_mes_comuna1),4)
mse_test_mes_comuna1 <- round(MSE(test_mes_comuna$ACCIDENTALIDAD,
                                  y_est_test_mes_comuna1),4)

# Modelo 2
y_est_train_mes_comuna2 <- round(predict(mod_mes_comuna2, newdata = train_mes_comuna,
                                         type = "response"),0)
y_est_test_mes_comuna2 <- round(predict(mod_mes_comuna2, newdata = test_mes_comuna,
                                        type = "response"),0)
mse_train_mes_comuna2 <- round(MSE(train_mes_comuna$ACCIDENTALIDAD,
                                   y_est_train_mes_comuna2),4)
mse_test_mes_comuna2 <- round(MSE(test_mes_comuna$ACCIDENTALIDAD,
                                  y_est_test_mes_comuna2),4)

Modelo_Mixto_Poisson Train_MSE Test_MSE Porcentaje_Variacion
ACCIDENTALIDAD ~ 1 + (1 | COMUNA/CLASE) 88.9306 94.6510 3.12
ACCIDENTALIDAD ~ DIA_FESTIVO + (1 | COMUNA/CLASE) 87.6868 91.3359 2.04
ACCIDENTALIDAD ~ DIA_FESTIVO + (1 + DIA_FESTIVO | COMUNA/CLASE) 87.6793 91.0191 1.87

Aunque el modelo mod_mes_comuna2 no tiene una mejora muy significativa respecto al modelo mod_mes_comuna1, sigue siendo mejor con el críterio del Test_MSE, por lo que el modelo elegido es el modelo mod_mes_comuna2

\[\widehat{\text{ACCIDENTALIDAD}} = \text{DIA_FESTIVO} + (1 + \text{DIA_FESTIVO} | \text{COMUNA/CLASE})\] A continuación se enseña la comparación entre la accidentalidad observada y la accidentalidad predicha mensualmente para la base de prueba (columna final).

data.frame(test_mes_comuna, A.PREDICHA = y_est_test_mes_comuna2)

5.3 Modelos predictivos para los Barrios

La base de datos para realizar el ajuste de los barrios se obtuvo igual a como se realizó para las comunas, es decir, con un expand.grid pero esta vez agrupando la base de datos por barrios.

Realizando el mismo método visto anteriormente para la selección de modelos en las comunas, se encuentran los siguiente modelos para los barrios.

5.3.1 Modelo - unidad de tiempo Día

Base de datos agrupada para la accidentalidad (número de accidentes) diaria.

accidentes_dia_barrio # Base de datos diaria

Se encontró que el modelo que mejor explica esta accidentalidad es:

LS0tDQp0aXRsZTogIlJlcG9ydGUgVMOpY25pY28iDQphdXRob3I6ICJKYWltZSBBbmRyZXMgTW9saW5hIENvcnJlYSA8YnI+IFZhbGVudGluYSBHYXJjw61hIFZlbGFzcXVleiA8YnI+IEZlbGlwZSBWaWxsYXJyZWFsIFBpZWRyYWhpdGEgPGJyPiBSaWNhcmRvIFBlbmFsb3phIFZlbGFzcXVleiA8YnI+IERhbmllbCBDaGFuY2kgUmVzdHJlcG8iDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiBrYXRlDQogICAgY3NzOiBmb3JtYXQuY3NzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdG9jOiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogVFJVRQ0KLS0tDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGVmLmNodW5rLmhvb2sgIDwtIGtuaXRyOjprbml0X2hvb2tzJGdldCgiY2h1bmsiKQ0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KGNodW5rID0gZnVuY3Rpb24oeCwgb3B0aW9ucykgew0KICB4IDwtIGRlZi5jaHVuay5ob29rKHgsIG9wdGlvbnMpDQogIGlmZWxzZShvcHRpb25zJHNpemUgIT0gIm5vcm1hbHNpemUiLCBwYXN0ZTAoIlxuIFxcIiwgb3B0aW9ucyRzaXplLCJcblxuIiwgeCwgIlxuXG4gXFxub3JtYWxzaXplIiksIHgpDQp9KQ0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgc2l6ZT0iZm9vdG5vdGVzaXplIiwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRiwgd2FybmluZyA9IEYpDQpgYGANCg0KDQojIEludHJvZHVjY2nDs24NCg0KDQojIE9iamV0aXZvcw0KDQpFbCBhbsOhbGlzaXMgeSBsb3MgbW9kZWxvcyBxdWUgc2UgcHJldGVuZGVuIGNvbnN0cnVpciBzw7NsbyBhYmFyY2FuIGVsIMOhcmVhIHVyYmFuYSBkZSBNZWRlbGzDrW4gY29ycmVzcG9uZGllbnRlIGEgc3VzIDE2IGNvbXVuYXMgeSBzdXMgcmVzcGVjdGl2b3MgYmFycmlvcy4NCg0KIyMgR2VuZXJhbA0KDQpDb25zdHJ1aXIgdW4gc2lzdGVtYSBxdWUgbGUgcGVybWl0YSBhIGxhIFNlY3JldGFyw61hIGRlIE1vdmlsaWRhZCBkZSBNZWRlbGzDrW4gdG9tYXIgZGVjaXNpb25lcyBlbiBsYSBjcmVhY2nDs24gZGUgZXN0cmF0ZWdpYXMgcXVlIGF5dWRlbiBhIHJlZHVjaXIgbGEgYWNjaWRlbnRhbGlkYWQgZW4gem9uYSBlc3BlY8OtZmljYXMgZGUgTWVkZWxsw61uLCBvYnNlcnZhbmRvIGN1YWxlcyBzb24gbG9zIHRpcG9zIGRlIGFjY2lkZW50ZXMgbcOhcyBmcmVjdWVudGVzIGVuIGxhcyBkaWZlcmVudGVzIGNvbXVuYXMgeSBzdXMgYmFycmlvcy4NCg0KVGFtYmnDqW4gc2UgYnVzY2EgcXVlIGVsIHNpc3RlbWEgbGVzIHBlcm1pdGEgYSB0b2RvcyBsb3MgY2l1ZGFkYW5vcyBxdWUgc2UgbW92aWxpemFuIGVuIHVuIHZlaMOtY3VsbyB0b21hciBwcmVjYXVjaW9uZXMgcmVzcGVjdG8gYSBsb3MgYWNjaWRlbnRlcyBtw6FzIGZyZWN1ZW50ZXMgcXVlIHN1Y2VkZW4gZW4gbHVnYXJlcyBlc3BlY8OtZmljb3MuDQoNCkxvIGFudGVyaW9yIGNyZWFuZG8gdW5hIGludGVyZmF6IGFtaWdhYmxlIHBhcmEgbG9zIHVzdWFyaW9zIHkgZsOhY2lsIGRlIHV0aWxpemFyLg0KDQojIyBFc3BlY8OtZmljb3MNCg0KLSBDb25zdHJ1aXIgbW9kZWxvcyBxdWUgcHJlZGlnYW4gZWwgbsO6bWVybyBkZSBhY2NpZGVudGVzIGRlIGNhZGEgdGlwbywgdG9tYW5kbyBjb21vIGVudHJhZGFzIHVuYSB2ZW50YW5hIHkgdW5hIHJlc29sdWNpw7NuIHRlbXBvcmFsIGVzcGVjw61maWNhcywgYWRlbcOhcyBkZSB1bmEgem9uYSBlc3BhY2lhbCBxdWUgcHVlZGUgc2VyIHVuIGJhcnJpbyBvIGNvbXVuYSBkZSBNZWRlbGzDrW4uDQoNCi0gRWxhYm9yYXIgdW4gc2lzdGVtYSBxdWUgYWdydXBlIGJhcnJpb3MgY29uIGNhcmFjdGVyw61zdGljYXMgc2ltaWxhcmVzIHBvciBsb3MgdGlwb3MgZGUgYWNjaWRlbnRlcyAoY2xhc2UpIHkgcXVlIHRpZW5lbiBsdWdhciBlbiBlc3Rvcy4NCg0KLSBDcmVhciB1bmEgaW50ZXJmYXogZ3LDoWZpY2EgcXVlIGxlIHBlcm1pdGEgYWwgdXN1YXJpbyB2aXN1YWxpemFyIGNvbiBtYXlvciBjb21vZGlkYWQgbGEgYWNjaWRlbnRhbGlkYWQgcXVlIGhheSBlbiBNZWRlbGzDrW4geSBsYXMgcHJlZGljY2lvbmVzIGRlIGVzdGEgYSBmdXR1cm8uDQoNCg0KDQojIERlc2NyaXBjacOzbiB5IExpbXBpZXphIGRlIGxvcyBkYXRvcw0KDQpMb3MgZGF0b3MgdXRpbGl6YWRvcyBzZSBlbmN1ZW50cmFuIGVuIGxhcyBiYXNlcyBkZSBkYXRvcyBkZSBHZW9tZWRlbGzDrW4gKFBvcnRhbCBHZW9ncsOhZmljbyBkZWwgTXVuaWNpcGlvIGRlIE1lZGVsbMOtbikgeSBjb250aWVuZW4gaW5mb3JtYWNpw7NuIHJlZmVyZW50ZSBhIG3Dumx0aXBsZXMgYWNjaWRlbnRlcyBkZSB0csOhbnNpdG8sIGVuIGxvcyBxdWUgc2UgZGV0YWxsYSBlbCB0aXBvIGRlIGFjY2lkZW50ZSwgZMOzbmRlIHkgY3XDoW5kbyBvY3VycmnDsy4gTGEgYmFzZSBkZSBkYXRvcyB1dGlsaXphZGEgZW4gZXN0ZSB0cmFiYWpvIGVzIHVuYSB1bmnDs24gZGUgbGFzIGJhc2VzIGRlIGluY2lkZW50ZXMgZW4gMjAxNCwgMjAxNSwgMjAxNiwgMjAxNyB5IDIwMTgsIHNlIHVzYXLDoSBlc3RlIMO6bHRpbW8gYcOxbyBwYXJhIHZhbGlkYXIgbG9zIG1vZGVsb3MgcGxhbnRlYWRvcy4NCg0KTGEgYmFzZSBkZSBkYXRvcyBvcmlnaW5hbCBjdWVudGEgY29uIGxhcyBzaWd1aWVudGVzIHZhcmlhYmxlczoNCg0KLSBYOiBDb21wb25lbnRlIGRlIGNvb3JkZW5hZGEuDQotIFk6IENvbXBvbmVudGUgZGUgY29vcmRlbmFkYS4NCi0gT0JKRUNUSUQ6IElkIGRlIGNhZGEgaW5jaWRlbnRlLg0KLSBSQURJQ0FETzogQ8OzZGlnbyBlbWl0aWRvIHBvciBsYSBzZWNyZXRhcsOtYSBkZSBtb3ZpbGlkYWQgZGUgTWVkZWxsw61uLg0KLSBGRUNIQTogRmVjaGEgZGVsIGluY2lkZW50ZS4NCi0gSE9SQTogSG9yYSBkZWwgaW5jaWRlbnRlLg0KLSBESUE6IETDrWEgZGVsIG1lcyBlbiBlbCBxdWUgb2N1cnJlIGVsIGluY2lkZW50ZQ0KLSBQRVJJT0RPOiBBw7FvIGRlbCBpbmNpZGVudGUuDQotIENMQVNFOiBUaXBvIGRlIGFjY2lkZW50ZQ0KLSBESVJFQ0NJT04uDQotIERJUkVDQ0lPTl9FTkMuDQotIENCTUw6IEPDs2RpZ28gZGUgdWJpY2FjacOzbiBkZWwgcHJlZGlvIGVuIGxhIGNpdWRhZC4NCi0gVElQT19HRU9DT0Q6IFRpcG8gZGUgDQotIEdSQVZFREFEOiByZXBlcmN1c2lvbmVzIGRlbCBhY2NpZGVudGUNCi0gQkFSUklPLg0KLSBDT01VTkEuDQotIERJU0VOTzogY2xhc2lmaWNhY2nDs24gZGVsIGx1Z2FyIGRlbCBhY2NpZGVudGUuDQotIERJQV9OT01CUkU6IETDrWEgZGUgbGEgc2VtYW5hIGVuIGVsIHF1ZSBvY3VycmUgZWwgaW5jaWRlbnRlDQotIE1FUzogTWVzIGRlbCBpbmNpZGVudGUgZW4gbsO6bWVybyAoZGVsIDEgYWwgMTIpDQotIE1FU19OT01CUkU6IE1lcyBkZWwgaW5jaWRlbnRlDQotIFhfTUFHTkFNRUQ6IENvbXBvbmVudGUgZGUgY29vcmRlbmFkYQ0KLSBZX01BR05BTUVEOiBDb21wb25lbnRlIGRlIGNvb3JkZW5hZGENCi0gTE9OR0lUVUQ6IENvbXBvbmVudGUgZGUgY29vcmRlbmFkYQ0KLSBMQVRJVFVEOiBDb21wb25lbnRlIGRlIGNvb3JkZW5hZGENCg0KDQpTZSBkZWNpZGVuIGVsaW1pbmFyIGxhcyB2YXJpYWJsZXMgIlgiLCAiWSIsICJYX01BR05BTUVEIiB5ICJZX01BR05BTUVEIiB5YSBxdWUgY3VtcGxlbiBsYSBtaXNtYSBmdW5jacOzbiBxdWUgbGFzIHZhcmlhYmxlcyAiTE9OR0lUVUQiIHkgIkxBVElUVUQiLiBUYW1iacOpbiBzZSBlbGltaW5hIGxhIHZhcmlhYmxlICJSQURJQ0FETyIgeWEgcXVlIHNpcnZlIHBhcmEgaWRlbnRpZmljYXIgYSB1biByZXNwZWN0aXZvIGluY2lkZW50ZSwgYWwgaWd1YWwgcXVlIGxhIHZhcmlhYmxlICJPQkpFQ1RJRCIuIFNlIGVsaW1pbmEgbGEgdmFyaWFibGUgIk1FU19OT01CUkUiIHlhIHF1ZSBlcyByZWR1bmRhbnRlIGVsIGxhIGJhc2UgZGUgZGF0b3MsIGRlYmlkbyBhIHF1ZSBzZSBlbmN1ZW50cmEgbGEgbWlzbWEgaW5mb3JtYWNpw7NuIGVuIGxhIHZhcmlhYmxlICJNRVMiLiBBZGVtw6FzIHBvbmVybGUgbG9zIG5vbWJyZXMgYSBwYXJ0aXIgZGUgbGEgdmFyaWFibGUgIk1FUyIgdHJhZXLDrWEgdW4gYXVtZW50byBhbCBjb3N0ZSBjb21wdXRhY2lvbmFsIGlubmVjZXNhcmlvLiBUYW1iacOpbiBzZSBkZWNpZGUgZWxpbWluYXIgbGFzIHZhcmlhYmxlcyAiRElSRUNDSU9OIiwgIkRJUkVDQ0lPTl9FTkMiLCAiQ0JNTCIsICJUSVBPX0dFT0NPRCIgeSAiSE9SQSIgeWEgbm8gc2Vyw6FuIGRlIHV0aWxpZGFkIGVuIGVsIG1vZGVsby4NCg0KQWRlbcOhcyBzZSBjcmVhIG90cmEgdmFyaWFibGUgbGxhbWFkYSAiRElBX0ZFU1RJVk8iLiBQYXJhIHBvZGVyIGFuYWxpemFyIGVsIG7Dum1lcm8gZGUgYWNjaWRlbnRlcyBlbiBsb3MgZMOtYXMgZmVzdGl2b3MgeSBkZW3DoXMgZmVjaGFzIGVzcGVjaWFsZXMuIFBvciBsbyB0YW50bywgbGFzIHZhcmlhYmxlcyBkZSBpbnRlcsOpcyBxdWUgc2UgdXNhbiBlbiBlc3RlIHByb3llY3RvIHNvbiBsYXMgc2lndWllbnRlczoNCg0KLSBPQkpFQ1RJRDogSWQgZGUgY2FkYSBpbmNpZGVudGUuDQotIENMQVNFOiBUaXBvIGRlIGFjY2lkZW50ZQ0KLSBHUkFWRURBRDogcmVwZXJjdXNpb25lcyBkZWwgYWNjaWRlbnRlDQotIENPTVVOQS4NCi0gQkFSUklPLg0KLSBESVNFTk86IGNsYXNpZmljYWNpw7NuIGRlbCBsdWdhciBkZWwgYWNjaWRlbnRlLg0KLSBMQVRJVFVEOiBDb21wb25lbnRlIGRlIGNvb3JkZW5hZGENCi0gTE9OR0lUVUQ6IENvbXBvbmVudGUgZGUgY29vcmRlbmFkYQ0KLSBGRUNIQTogRmVjaGEgZGVsIGluY2lkZW50ZS4NCi0gRElBOiBEw61hIGRlbCBtZXMgZW4gZWwgcXVlIG9jdXJyZSBlbCBpbmNpZGVudGUNCi0gTUVTOiBNZXMgZGVsIGluY2lkZW50ZSBlbiBuw7ptZXJvIChkZWwgMSBhbCAxMikNCi0gUEVSSU9ETzogQcOxbyBkZWwgaW5jaWRlbnRlLg0KLSBESUFfTk9NQlJFOiBEw61hIGRlIGxhIHNlbWFuYSBlbiBlbCBxdWUgb2N1cnJlIGVsIGluY2lkZW50ZQ0KLSBESUFfRkVTVElWTy4NCg0KRGVmaW5pZG8gbG8gYW50ZXJpb3IsIHNlIHJlYWxpemEgdW5hIGxpbXBpZXphIGVuIGxhIGJhc2UgZGUgZGF0b3MsIHlhIHF1ZSBoYXkgcmVnaXN0cm9zIHUgb2JzZXJ2YWNpb25lcyBxdWUgZXN0w6FuIG1hbCBlc2NyaXRvcyBvIHNpbXBsZW1lbnRlIG5vIGRlYmVyw61hbiBlc3Rhci4gQSBjb250aW51YWNpw7NuIHNlIGV4cGxpY2FuIGxvcyBjYW1iaW9zIHJlYWxpemFkb3MuDQoNCiMjIERlcHVyYWNpw7NuDQoNClNlIGRlY2lkZSBxdWl0YXIgbG9zIGFjZW50b3MgYSBjYWRhIHVuYSBkZSBsYXMgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyBkZWJpZG8gYSBxdWUgaGF5IHZhbG9yZXMgcmVwZXRpZG9zIGxvcyBjdWFsZXMgcmVwcmVzZW50YW4gZWwgbWlzbW8gbml2ZWwgbyBjYXRlZ29yaWEsIHBlcm8gY29uIGFjZW50byB5IHNpbiBhY2VudG8uIFVuIGVqZW1wbG8gZGUgZXN0byBlcyBlbCBiYXJyaW8gQmVybGluIGRlIGxhIGNvbXVuYSBBcmFuanVleiwgZWwgY3VhbCBhcGFyZWNlIDY0NyB2ZWNlcyBjb24gZWwgbm9tYnJlIGRlICJCZXJsaW4iIHkgMTUgdmVjZXMgY29uIGVsIG5vbWJyZSBkZSAiQmVybMOtbiIuIFBvciBsbyB0YW50byBzZSBkZWNpZGUgYXJyZWdsYXIgZXN0ZSBwcm9ibGVtYS4NCg0KIyMjIFBhcmEgQ09NVU5BDQoNCkxhIHZhcmlhYmxlIENPTVVOQSBkZWJlIHRlbmVyIGxhcyBjb211bmFzIHVyYmFuYXMgZGUgTWVkZWxsw61uIHkgc3VzIGNvcnJlZ2ltaWVudG9zLg0KDQpTZSBzYWJlIHF1ZSBlbiBNZWRlbGzDrW4gaGF5IDE2IGNvbXVuYXMgdXJiYW5hcyB5IDUgY29ycmVnaW1pZW50b3MsIGFtYm9zIGVzdMOhbiBjb21wdWVzdG9zIHBvciBiYXJyaW9zLCBwb3IgbG8gcXVlIGxhIHZhcmlhYmxlIGRlYmVyw61hIHRlbmVyIDE2ICsgNSA9IDIxIG5pdmVsZXMuIFBlcm8gYWwgb2JzZXJ2YXIgbG9zIG5pdmVsZXMgZGUgbGEgdmFyaWFibGUgIkNPTVVOQSIgZW4gZWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50bywgc2UgZXZpZGVuY2lhbiA4NCBjb211bmFzLCBwb3IgbG8gY3VhbCBzZSBkZWNpZGUgYnVzY2FyIGxhcyByYXpvbmVzIGRlIGVzdG8geSB0cmF0YXIgZGUgY29ycmVnaXJsby4NCg0KKipDb211bmFzOioqIFBvcHVsYXIsIFNhbnRhIENydXosIE1hbnJpcXVlLCBBcmFuanVleiwgQ2FzdGlsbGEsIERvY2UgZGUgT2N0dWJyZSwgUm9ibGVkbywgVmlsbGEgSGVybW9zYSwgQnVlbm9zIEFpcmVzLCBMYSBDYW5kZWxhcmlhLCBMYXVyZWxlcy1Fc3RhZGlvLCBMYSBBbcOpcmljYSwgU2FuIEphdmllciwgRWwgUG9ibGFkbywgR3VheWFiYWwsIEJlbMOpbi4NCg0KKipDb3JyZWdpbWllbnRvczoqKiBDb3JyZWdpbWllbnRvIGRlIFNhbiBDcmlzdMOzYmFsLCBDb3JyZWdpbWllbnRvIGRlIFNhbiBBbnRvbmlvIGRlIFByYWRvLCBDb3JyZWdpbWllbnRvIGRlIFNhbnRhIEVsZW5hLCBDb3JyZWdpbWllbnRvIGRlIEFsdGF2aXN0YSwgQ29ycmVnaW1pZW50byBkZSBTYW4gU2ViYXN0acOhbiBkZSBQYWxtaXRhcy4NCg0KVW5vIGRlIGxvcyBwcm9ibGVtYXMgcXVlIHNlIGVuY3VlbnRyYSBlcyBxdWUgbGEgcGVyc29uYSBlbmNhcmdhZGEgZGUgZGlnaXRhciBsb3MgZGF0b3Mgc2UgY29uZnVuZGnDsyBlbnRyZSBsYXMgdmFyaWFibGVzICJDT01VTkEiIHkgIkJBUlJJTyIuIFVuIGVqZW1wbG8gZGUgZXN0byBlcyBlbCBiYXJyaW8gQm9zdG9uIHF1ZSBhcGFyZWNlIGNvbW8gY29tdW5hIGVuIDIgb2JzZXJ2YWNpb25lcywgY29uIHN1IHJlc3BlY3RpdmEgY29tdW5hIChMYSBjYW5kZWxhcmlhKSBlbiBiYXJyaW8uIEFzw60gY29tbyBlbCBlamVtcGxvIGFudGVyaW9yLCBoYXkgdmFyaW9zIGNhc29zLiBQb3IgbG8gY3VhbCwgZXN0YXMgY29tdW5hcyB5IGJhcnJpb3Mgc2UgcG9uZW4gZW4gc3UgcG9zaWNpw7NuIGNvcnJlY3RhLg0KDQpMdWVnbyBkZSByZXNvbHZlciBlbCBwcm9ibGVtYSBhbnRlcmlvciwgc2UgZW5jdWVudHJhIHF1ZSBhw7puIGhheSBjb211bmEgcXVlIG5vIHBlcnRlbmVjZW4gYSBsYXMgMjEgbWVuY2lvbmFkYXMgYW50ZXJpb3JtZW50ZSwgZXN0YXMgc29uOiAiSW4iLCAiQVUiLCAiU04iLCAiMCIuIEFsIG5vIGVuY29udHJhciBpbmZvcm1hY2nDs24gZGUgZXN0b3MgdmFsb3JlcyBlbiBsYSBww6FnaW5hIG9maWNpYWwgbyBlbiBpbnRlcm5ldCwgc2UgZGVjaWRlIHJlZW1wbGF6YXIgZXN0b3MgdmFsb3JlcyBjb24gc3VzIHJlc3BlY3Rpdm9zIGJhcnJpb3MgY29tbyBkYXRvcyBmYWx0YW50ZXMgbyBOQS4NCg0KIyMjIFBhcmEgQkFSUklPDQoNClNlIGRldGVjdGEgcXVlIGxvcyBiYXJyaW9zIGNvbnRpZW5lbiBhbGd1bm9zIHByb2JsZW1hcyBkZSBlc3BhY2lvcywgYWxndW5vcyBlamVtcGxvcyBzb246DQoNCi0gIkFzb21hZGVyYSBOby4gMSIgeSAiQXNvbWFkZXJhIE5vLjEiDQotICJBdXJlcyBOby4gMiIgeSAiQXVyZXMgTm8uMiINCi0gIkJvbWJvbmEgTm8uIDEiIHkgIkJvbWJvbmEgTm8uMSINCi0gIkIuIENlcnJvICBFbCBWb2xhZG9yIiB5ICJCLiBDZXJybyBFbCBWb2xhZG9yIg0KDQpQb3IgbG8gdGFudG8gc2UgYXJyZWdsYSBlc3RlIHByb2JsZW1hIHF1aXRhbmRvIGVsIGVzcGFjaW8gcXVlIGhheSBlbnRyZSAiTm8uIiB5IHN1IG7Dum1lcm8gY29ycmVzcG9uZGllbnRlIGVuIGxvcyBiYXJyaW9zIHF1ZSB0aWVuZW4gZXN0YSBjYXJhY3RlcsOtc3RpY2EuIFRhbWJpw6luIHNlIGVsaW1pbmFuIGxvcyBlc3BhY2lvcyBxdWUgaGF5IGFudGVzIGRlIGxhIHByaW1lcmEgbGV0cmEgeSBkZXNwdcOpcyBkZSBsYSDDumx0aW1hIGxldHJhLg0KDQpUYW1iacOpbiBzZSByZWVtcGxhemEgIkJhcnJpb3MgZGUgSmVzw7pzIiBwb3IgIkJhcnJpbyBkZSBKZXPDunMiLCAiTnVldmEgVmlsbGEgZGUgTGEgSWd1YW5hIiBwb3IgIk51ZXZhIFZpbGxhIGRlIGxhIElndWFuYSIsICAiU2FudGEgTWFyw61hIGRlIExvcyDDgW5nZWxlcyIgcG9yICJTYW50YSBNYXLDrWEgZGUgbG9zIMOBbmdlbGVzIiwgIlZpbGxhIExpbGxpYW0iIHBvciAiVmlsbGEgTGlsaWFtIi4NCg0KIyMjIFBhcmEgQ0xBU0UNCg0KIkNhaWRhIGRlIE9jdXBhbnRlIiBzZSByZWVtcGxhemEgcG9yICJDYWlkYSBPY3VwYW50ZSIgeWEgcXVlIHNlIGNvbnNpZGVyYW4gZXF1aXZhbGVudGVzLiBUYW1iacOpbiBzZSBlbGltaW5hIGxhIG9ic2VydmFjacOzbiBjb24gQ0xBU0UgPSAiQ2hvcXVlIHkgQXRyb3BlbGxvIiB5YSBxdWUgc29sbyBoYXkgdW5hLg0KDQojIyMgUGFyYSBESUFfTk9NQlJFDQoNClNlIGVsaW1pbmFuIGxvcyBlc3BhY2lvcyBxdWUgaGF5IGFudGVzIGRlIGxhIHByaW1lcmEgbGV0cmEgeSBkZXNwdcOpcyBkZSBsYSDDumx0aW1hIGxldHJhIGVuIHRvZGFzIGxhcyBvYnNlcnZhY2lvbmVzLg0KDQojIyBJbXB1dGFjacOzbg0KDQpFbiBsb3MgZGF0b3MgZmFsdGFudGVzIGRlIGxhIHZhcmlhYmxlIERJU0VOTywgc2UgZGVjaWRlIHVzYXIgZWwgbm9tYnJlIGRlICJObyBSZWdpc3RyYWRvIiBwYXJhIHJlZW1wbGF6YXJsb3MuDQoNCkVuIGxhIGJhc2UgZGUgZGF0b3Mgc2UgdGllbmUgbGEgc2lndWllbnRlIGNhbnRpZGFkIGRlIGRhdG9zIGZhbHRhbnRlcyBwb3IgdmFyaWFibGU6IGxhIHZhcmlhYmxlIEJBUlJJTyB0aWVuZSAxOTk1NyBkZSBkYXRvcyBmYWx0YW50ZXMsIENPTVVOQSB0aWVuZSAxOTk1NyB5IENMQVNFIHRpZW5lIDcuIExhcyBkZW3DoXMgdmFyaWFibGVzIG5vIGNvbnRpZW5lbiBkYXRvcyBmYWx0YW50ZXMuDQoNCkNvbW8gbGEgdmFyaWFibGUgQ0xBU0Ugc29sbyB0aWVuZSA3IGRhdG9zIGZhbHRhbnRlcywgc2UgZGVjaWRlIGVsaW1pbmFyIGVzdGFzIG9ic2VydmFjaW9uZXMuIFBvciBlbCBjb250cmFyaW8sIGNvbW8gQkFSUklPIHkgQ09NVU5BIHByZXNlbnRhbiB1bmEgY2FudGlkYWQgaW1wb3J0YW50ZSBkZSBsb3MgZGF0b3MsIHNlIGRlY2lkZSByZWFsaXphciB1bmEgaW1wdXRhY2nDs24gZGUgbG9zIG1pc21vcy4NCg0KIyMjIFBhcmEgQ09NVU5BDQoNCg0KUHJpbWVybyBzZSBkZWNpZGUgcmVhbGl6YXIgbGEgaW1wdXRhY2nDs24gZW4gbGEgdmFyaWFibGUgQ09NVU5BLCBzZSB1dGlsaXphIHVuIG1vZGVsbyBrbm4gKGstTmVhcmVzdCBOZWlnaGJvdXIpIGNvbiB2YXJpYWJsZSByZXNwdWVzdGEgQ09NVU5BIHkgY29tbyBwcmVkaWN0b3JhcyBsYXMgdmFyaWFibGVzIExBVElUVUQgeSBMT05HSVRVRCBlc2NhbGFkYXMuIERhZG8gcXVlIGVzdGUgZXMgdW4gbW9kZWxvIHF1ZSB1c2EgbGEgZGlzdGFuY2lhIGV1Y2xpZGlhbmEgcGFyYSBjbGFzaWZpY2FyIG9ic2VydmFjaW9uZXMsIHNlIHBpZW5zYSBxdWUgZnVuY2lvbmFyw61hIGJpZW4gcGFyYSBjbGFzaWZpY2FyIGEgbGFzIGNvbXVuYXMgdGVuaWVuZG8gc3UgdWJpY2FjacOzbiBlbiBsYXRpdHVkIHkgbG9uZ2l0dWQuDQoNClBhcmEgYWp1c3RhciBlbCBtb2RlbG8gc2UgdXPDsyBsYSBmdW5jacOzbiBgdHJhaW5gIGRlbCBwYXF1ZXRlIGBjYXJldGAsIHVzYW5kbyBLLUZvbGQgQ1YgY29uICpLPTEwKiB5IGVsIG7Dum1lcm8gZGUgdmVjaW5vcyBpZ3VhbCBhIDEsIDQsIDcsIDEwLCAxMywgMTYgeSAxOS4gU2UgZW50cmVuw7MgZWwgbW9kZWxvIHVzYW5kbyBsb3MgYcOxb3MgZGUgMjAxNCBhIDIwMTcgeSBzZSB2YWxpZMOzIGNvbiBlbCBhw7FvIDIwMTguIFNlIHVzw7MgbGEgcHJlY2lzacOzbiBkZSBwcnVlYmEgZGVsIG1vZGVsbyBjb21vIGZ1bmNpw7NuIGRlIG9wdGltaXphY2nDs24uIExvcyByZXN1bHRhZG9zIG9idGVuaWRvcyBzb24gbG9zIHNpZ3VpZW50ZXM6DQoNClxiZWdpbnt0YWJsZX1baF0NClxiZWdpbntjZW50ZXJ9DQpcYmVnaW57dGFidWxhcn17fGN8Y3xjfGN8Y3xjfH0NClxobGluZQ0KQWNjdXJhY3kgJiBLYXBwYSAgJiBBY2N1cmFjeUxvd2VyICYgQWNjdXJhY3lVcHBlciAmIEFjY3VyYWN5TnVsbCAmIEFjY3VyYWN5UFZhbHVlIFxcDQpcaGxpbmUNCjAuOTk4NSAmIDAuOTk4NCAmIDAuOTk4MSAmIDAuOTk4OSAmIDAuMjA2MiAmIDAgXFwNClxobGluZSAgICAgICAgIA0KXGVuZHt0YWJ1bGFyfQ0KXGVuZHtjZW50ZXJ9DQpcZW5ke3RhYmxlfQ0KDQoNCiAqKkFjY3VyYWN5KiogfCAqKkthcHBhKiogfCAqKkFjY3VyYWN5TG93ZXIqKiB8ICoqQWNjdXJhY3lVcHBlcioqIHwgKipBY2N1cmFjeU51bGwqKiB8ICoqQWNjdXJhY3lQVmFsdWUqKiB8DQotLS0tLS0tLS0tLS0tfCAtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18IC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLXwgLS0tLS0tLS0tLS0tLXwNCjAuOTk4NSB8IDAuOTk4NCB8IDAuOTk4MSB8IDAuOTk4OSB8IDAuMjA2MiB8IDAgIA0KDQohW10ocHJlY2lzaW9uX2NvbXVuYV9OQS5wbmcpeyNpZCAuY2xhc3Mgd2lkdGg9NTAlIGhlaWdodD01MCV9DQoNCg0KTG9zIHJlc3VsdGFkb3Mgc2UgY29uc2lkZXJhbiBidWVub3MgeSBzZSBkZWNpZGUgcmVhbGl6YXIgZWwgcmVlbXBsYXpvIGRlIGxvcyB2YWxvcmVzIGZhbHRhbnRlcyBkZSBDT01VTkEgcG9yIG1lZGlvIGRlIGVzdGUgbW9kZWxvLg0KDQpcbmV3cGFnZQ0KDQojIyMgUGFyYSBCQVJSSU8NCg0KU2UgZGVjaWRlIHVzYXIgdW4gbW9kZWxvIGtubiAoay1OZWFyZXN0IE5laWdoYm91cikgY29uIHZhcmlhYmxlIHJlc3B1ZXN0YSBCQVJSSU8geSBjb21vIHByZWRpY3RvcmFzIGxhcyB2YXJpYWJsZXMgTEFUSVRVRCB5IExPTkdJVFVEIGVzY2FsYWRhcyB5IENPTVVOQS4NCg0KRGFkbyBxdWUgaGFiw61hbiB2YWxvcmVzIGRlIGJhcnJpb3MgcXVlIGVzdMOhbiBlbiBlbCBhw7FvIDIwMTggcGVybyBubyBlbiBsb3MgZGVtw6FzLCBubyBzZSBwdWRvIGhhY2VyIHVzbyBkZWwgcGFxdWV0ZSBgY2FyZXRgIHlhIHF1ZSBwcmVzZW50YWJhIHVuIGVycm9yIGFsIGVudHJlbmFyIGVsIG1vZGVsbywgcG9yIGxvIHF1ZSBzZSBkZWNpZGUgdXNhciAgbGEgZnVuY2nDs24gYGtubi5jdmAgZGVsIHBhcXVldGUgYGNsYXNzYCwgcGFyYSBtZWRpciBlbCBjb21wb3J0YW1pZW50byBkZWwgbW9kZWxvIGtubiBjb24gayA9IDEgbWVkaWFudGUgTE9PQ1YsIGVudHJlbmFuZG8gZWwgbW9kZWxvIGNvbiBsb3MgYcOxb3MgZGUgMjAxNCBoYXN0YSAyMDE3IHkgdmFsaWRhbmRvIGVsIHJlc3VsdGFkbyBjb24gZWwgYcOxbyAyMDE4LiBTZSBvYnRpZW5lIHVuYSBwcmVjaXNpw7NuIGRlIHBydWViYSBkZSAwLjk5NzUsIHBvciBsbyBxdWUgc2UgZGVjaWRlIHVzYXIgZXN0ZSBtb2RlbG8gcGFyYSBpbXB1dGFyIGxvcyBkYXRvcyBmYWx0YW50ZXMgZW4gQkFSUklPLg0KDQojIyBCYXNlIGZpbmFsDQoNCkZpbmFsbWVudGUgc2UgZGVjaWRlIHVzYXIgc29sbyBsYXMgY29tdW5hcyBkZWwgw6FyZWEgdXJiYW5hIGRlIE1lZGVsbMOtbiB5IGRlamFyIGEgdW4gbGFkbyBsb3MgY29ycmVnaW1pZW50b3MsIGVzIGRlY2lyLCBzb2xvIHNlIHRlbmRyw6FuIGxhcyAxNiBjb211bmFzIG1lbmNpb25hZGFzIGFudGVyaW9ybWVudGUgZW4gZXN0ZSByZXBvcnRlLg0KDQpUcmFzIGxhcyBtb2RpZmljYWNpb25lcyBtZW5jaW9uYWRhcywgZGUgbGFzIDIyODY5MyBvYnNlcnZhY2lvbmVzIG9yaWdpbmFsZXMgZW50cmUgbGFzIGJhc2VzIGRlIGRhdG9zIGRlbCAyMDE0IGFsIDIwMTgsIHNlIHV0aWxpemFyw6FuIDIwNDAwMiBvYnNlcnZhY2lvbmVzIGVuIGVsIGFuw6FsaXNpcyB5IGNvbnN0cnVjY2nDs24gZGUgbG9zIG1vZGVsb3MuIExhIGJhc2UgZGUgZGF0b3MgbHVjZSBkZSBlc3RhIG1hbmVyYToNCg0KYGBge3IsIHJvd3MucHJpbnQgPSA1fQ0KZGYgPC0gcmVhZC5jc3YoZmlsZSA9ICJCYXNlX2RlZmluaXRpdmEuY3N2IiwgaGVhZGVyID0gVCwgbnJvd3MgPSAxMDAwMCwNCiAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBUKQ0KZGYkRkVDSEEgPC0gYXMuRGF0ZShkZiRGRUNIQSkNCmRmDQpgYGANCg0KQSBjb250aW51YWNpw7NuIHNlIGVuY3VlbnRyYSBlbCBhY2Nlc28gYWwgY8OzZGlnbyB1dGlsaXphZG8gZW4gdG9kbyBlc3RlIHByb2Nlc28NCg0KRW5sYWNlOiBodHRwczovL2dpdGh1Yi5jb20vdmFnYXJjaWF2ZS9Qcm9qZWN0X3gvYmxvYi9tYXN0ZXIvbGltcGllemEvRGVwdXJhY2lvbi5SbWQNCg0KIyBBbsOhbGlzaXMgZGVzY3JpcHRpdm8NCg0KDQojIE1vZGVsb3MgUHJlZGljdGl2b3MNCg0KU2UgZGVmaW5lIGxhIGFjY2lkZW50YWxpZGFkIGNvbW8gZWwgbsO6bWVybyBkZSBhY2NpZGVudGVzIHF1ZSBoYXkgYSBuaXZlbCBkaWFyaW8sIHNlbWFuYWwgeSBtZW5zdWFsLCBkaXNjcmltaW5hbmRvIHBvciB0aXBvIGRlIGFjY2lkZW50ZSAoQ0xBU0UpIHkgYmFycmlvIG8gY29tdW5hIGRvbmRlIMOpc3RlIG9jdXJyZS4gTG8gYW50ZXJpb3IgZGViaWRvIGEgcXVlIGV4aXN0ZSB1bmEgdmFyaWFiaWxpZGFkIG5vdGFibGUgYSBuaXZlbCBkZSBhY2NpZGVudGFsaWRhZCBwYXJhIGNhZGEgY29sdW1uYSB5IHBhcmEgY2FkYSBiYXJyaW8gY29tbyBzZSBvYnNlcnbDsyBlbiBlbCBhbsOhbGlzaXMgZGVzY3JpcHRpdm8uDQoNCkRhZG8gcXVlIGVsIG9iamV0aXZvIGVzIHByZWRlY2lyIGxhIGFjY2lkZW50YWxpZGFkIGEgbml2ZWwgZGlhcmlvLCBtZW5zdWFsIG8gc2VtYW5hbCwgc2UgYnVzY2EgY29uc3RydWlyIG1vZGVsb3MgcXVlIHByZWRpZ2FuIGVsIG7Dum1lcm8gZGUgYWNjaWRlbnRlcyBlbiBjYWRhIHVubyBkZSBsb3MgcmFuZ29zIHRlbXBvcmFsZXMgZXMgZGVjaXIgc2UgY3JlYXLDoW4gbW9kZWxvcyBlc3BlY8OtZmljb3MgcGFyYSBjYWRhIGNvbXVuYS9iYXJyaW8gc2Vnw7puIGxhIHVuaWRhZCB0ZW1wb3JhbC4NCg0KU2UgcGllbnNhIHF1ZSBsb3MgbW9kZWxvcyBtaXh0b3MgbyBtb2RlbG9zIGplcsOhcnF1aWNvcyBwdWVkZW4gcmVwcmVzZW50YXIgYmllbiBlc3RhIGRlZmluaWNpw7NuIGRlIGFjY2lkZW50YWxpZGFkLCBkZWJpZG8gYSBsYSBhZ3J1cGFjacOzbiBxdWUgZXhpc3RlIGFsIG1vbWVudG8gZGUgZGVmaW5pciBsYSBhY2NpZGVudGFsaWRhZCwgZXMgZGVjaXIsIGV4aXN0ZSB1bmEgYWdydXBhY2nDs24gZW50cmUgY29tdW5hIHkgY2xhc2U7IHkgb3RyYSBhZ3J1cGFjacOzbiBlbnRyZSBiYXJyaW8geSBjbGFzZS4gUG9yIGxvIHF1ZSBzZSBkZWNpZGUgY3JlYXIgZXN0b3MgbW9kZWxvcyBjb24gbGEgYXl1ZGEgZGVsIHBhcXVldGUgYGxtZTRgIHkgbWVkaXIgc3UgYWp1c3RlLiBFbiBjYWRhIHVubyBkZSBlc3RvcyBjYXNvcyBzZSBjcmVhIHVuIG1vZGVsbyBxdWUgcHJlZGlnYSBlbCBuw7ptZXJvIGRlIGFjY2lkZW50ZXMsIHBvciBsbyBxdWUgc2UgY29uc2lkZXJhIHF1ZSBsYSBhY2NpZGVudGFsaWRhZCBwdWVkZSBzZWd1aXIgdW5hIGRpc3RyaWJ1Y2nDs24gKlBvaXNzb24qLg0KDQpQYXJhIGV2YWx1YXIgbG9zIG1vZGVsb3Mgc2UgY3JlYW4gY29uanVudG9zIGRlIGVudHJlbmFtaWVudG8geSBjb25qdW50b3MgZGUgdmFsaWRhY2nDs24uIExvcyBjb25qdW50b3MgZGUgZW50cmVuYW1pZW50byBjb25zdGFuIGRlIHRvZGFzIGxhcyBjb21iaW5hY2lvbmVzIHBvc2libGVzIGRlIHRpcG9zIGRlIGFjY2lkZW50ZXMgc2Vnw7puIHNpIGVzIGNvbXVuYSBvIGJhcnJpbyB5IHN1IHVuaWRhZCBkZSB0aWVtcG8gY29uIGxvcyBhw7FvcyAocGVyaW9kb3MpIGRlbCAyMDE0IGFsIDIwMTcsIGVsIGNvbmp1bnRvIGRlIHZhbGlkYWNpw7NuIHNlcsOhIGNvbiBlbCBhw7FvIDIwMTguDQoNCkxhIG1lZGlkYSBwYXJhIGV2YWx1YXIgZWwgYWp1c3RlIGRlIGxvcyBtb2RlbG9zIHNlcsOhIGVsIGVycm9yIGN1YWRyw6F0aWNvIG1lZGlvIGVuIGxhcyBwcmVkaWNjaW9uZXMgdGFudG8gcGFyYSBsb3MgY29uanVudG9zIGRlIGVudHJlbmFtaWVudG8gY29tbyBwYXJhIGxvcyBkZSB2YWxpZGFjacOzbi4NCg0KJCRNU0UgPSBcZnJhY3tcc3VtX3tpPTF9Xk4gKGFjY2lkZW50YWxpZGFkX2kgLSBcd2lkZWhhdHthY2NpZGVudGFsaWRhZF9pKV4yfX17Tn0kJA0KDQpDb24gbGEgYXl1ZGEgZGUgUiBzZSBjcmVhIGxhIHNpZ3VpZW50ZSBmdW5jacOzbiBwYXJhIG9idGVuZXJsbzoNCg0KYGBge3J9DQpNU0UgPC0gZnVuY3Rpb24oeSwgeV9lc3QpIG1lYW4oKHkteV9lc3QpKioyKQ0KYGBgDQoNClNlIHVzYXLDoW4gbG9zIHNpZ3VpZW50ZXMgcGFxdWV0ZXMgcGFyYSBhanVzdGFyIGxvcyBtb2RlbG9zDQoNCmBgYHtyLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0NCmxpYnJhcnkobG1lNCkgICAgICAjIFBhcXVldGUgcGFyYSBsYSBjcmVhY2nDs24gZGUgbW9kZWxvcyBtaXh0b3MgcG9pc3Nvbg0KbGlicmFyeSh0aWR5dmVyc2UpICMgUGFxdWV0ZSBwYXJhIGxhIGNyZWFjacOzbiBkZSBsb3MgY29uanVudG9zIGRlIGRhdG9zDQpgYGANCg0KIyMgQWp1c3RlIGRlIGxhIGJhc2UgZGUgZGF0b3MNCg0KU2UgZGViZSBtb2RpZmljYXIgbGEgYmFzZSBkZSBkYXRvcyBkZXB1cmFkYSBwYXJhIHBvZGVyIHJlYWxpemFyIGxvcyBtb2RlbG9zLiBTZSBjcmVhbiB2YXJpYWJsZXMgZGUgdGllbXBvIGEgbml2ZWwgZGlhcmlvLCBzZW1hbmFsIHkgbWVuc3VhbCwgYWRlbcOhcyBkZSBsYSB2YXJpYWJsZSBhY2NpZGVudGFsaWRhZCB5IGTDrWFzIGZlc3Rpdm9zLg0KDQpgYGB7ciwgZXZhbCA9IEZ9DQojICBWZWN0b3IgY29uIGZlY2hhcyBkZSBkaWFzIGZlc3Rpdm9zDQogICMgRGVmaW5pciBkaWFzIGZlc3Rpdm9zDQogIGZlc3Rpdm9zIDwtIHltZChjKA0KICAgICMyMDE0DQogICAgJzIwMTQtMDEtMDEnLCcyMDE0LTAxLTA2JywnMjAxNC0wMy0yNCcsJzIwMTQtMDQtMTcnLCcyMDE0LTA0LTE4JywgJzIwMTQtMDUtMDEnLA0KICAgICcyMDE0LTA2LTAyJywnMjAxNC0wNi0yMycsJzIwMTQtMDYtMzAnLCcyMDE0LTA3LTIwJywnMjAxNC0wOC0wNycsICcyMDE0LTA4LTE4JywNCiAgICAnMjAxNC0xMC0xMycsJzIwMTQtMTEtMDMnLCcyMDE0LTExLTE3JywnMjAxNC0xMi0wOCcsJzIwMTQtMTItMjUnLA0KICAgICMyMDE1DQogICAgJzIwMTUtMDEtMDEnLCcyMDE1LTAxLTEyJywnMjAxNS0wMy0yMycsJzIwMTUtMDMtMjknLCcyMDE1LTA0LTAyJywnMjAxNS0wNC0wMycsDQogICAgJzIwMTUtMDQtMDUnLCcyMDE1LTA1LTAxJywnMjAxNS0wNS0xOCcsJzIwMTUtMDYtMDgnLCcyMDE1LTA2LTE1JywnMjAxNS0wNi0yOScsDQogICAgJzIwMTUtMDctMjAnLCcyMDE1LTA4LTA3JywgJzIwMTUtMDgtMTcnLCcyMDE1LTEwLTEyJywnMjAxNS0xMS0wMicsJzIwMTUtMTEtMTYnLA0KICAgICcyMDE1LTEyLTA4JywnMjAxNS0xMi0yNScsDQogICAgIzIwMTYNCiAgICAnMjAxNi0wMS0wMScsJzIwMTYtMDEtMTEnLCcyMDE2LTAzLTIwJywnMjAxNi0wMy0yMScsJzIwMTYtMDMtMjQnLCcyMDE2LTAzLTI1JywNCiAgICAnMjAxNi0wMy0yNycsJzIwMTYtMDUtMDEnLCcyMDE2LTA1LTA5JywnMjAxNi0wNS0zMCcsJzIwMTYtMDYtMDYnLCcyMDE2LTA3LTA0JywNCiAgICAnMjAxNi0wNy0yMCcsJzIwMTYtMDgtMDcnLCAnMjAxNi0wOC0xNScsJzIwMTYtMTAtMTcnLCcyMDE2LTExLTA3JywnMjAxNi0xMS0xNCcsDQogICAgJzIwMTYtMTItMDgnLCcyMDE2LTEyLTI1JywNCiAgICAjMjAxNw0KICAgICcyMDE3LTAxLTAxJywnMjAxNy0wMS0wOScsJzIwMTctMDMtMjAnLCcyMDE3LTA0LTA5JywnMjAxNy0wNC0xMycsJzIwMTctMDQtMTQnLA0KICAgICcyMDE3LTA0LTE2JywnMjAxNy0wNS0wMScsJzIwMTctMDUtMjknLCcyMDE3LTA2LTE5JywnMjAxNy0wNi0yNicsJzIwMTctMDctMDMnLA0KICAgICcyMDE3LTA3LTIwJywnMjAxNy0wOC0wNycsICcyMDE3LTA4LTIxJywnMjAxNy0xMC0xNicsJzIwMTctMTEtMDYnLCcyMDE3LTExLTEzJywNCiAgICAnMjAxNy0xMi0wOCcsJzIwMTctMTItMjUnLA0KICAgICMyMDE4DQogICAgJzIwMTgtMDEtMDEnLCcyMDE4LTAxLTA4JywnMjAxOC0wMy0xOScsJzIwMTgtMDMtMjUnLCcyMDE4LTAzLTI5JywnMjAxOC0wMy0zMCcsDQogICAgJzIwMTgtMDQtMDEnLCcyMDE4LTA1LTAxJywnMjAxOC0wNS0xNCcsJzIwMTgtMDYtMDQnLCcyMDE4LTA2LTExJywnMjAxOC0wNy0wMicsDQogICAgJzIwMTgtMDctMjAnLCcyMDE4LTA4LTA3JywgJzIwMTgtMDgtMjAnLCcyMDE4LTEwLTE1JywnMjAxOC0xMS0wNScsJzIwMTgtMTEtMTEnLA0KICAgICcyMDE4LTEyLTA4JywnMjAxOC0xMi0yNScsDQogICAgIzIwMTkNCiAgICAnMjAxOS0wMS0wMScsJzIwMTktMDEtMDcnLCcyMDE5LTAzLTI1JywnMjAxOS0wNC0xOCcsJzIwMTktMDQtMTknLCcyMDE5LTA1LTEnLA0KICAgICcyMDE5LTA2LTAzJywnMjAxOS0wNi0yNCcsJzIwMTktMDctMDEnLCcyMDE5LTA3LTIwJywnMjAxOS0wOC0wNycsJzIwMTktMDgtMTknLA0KICAgICcyMDE5LTEwLTE0JywnMjAxOS0xMS0wNCcsJzIwMTktMTEtMTEnLCcyMDE5LTEyLTA4JywnMjAxOS0xMi0yNScsDQogICAgIzIwMjANCiAgICAnMjAyMC0wMS0wMScsJzIwMjAtMDEtMDYnLCcyMDIwLTAzLTIzJywnMjAyMC0wNC05JywnMjAyMC0wNC0xMCcsJzIwMjAtMDUtMDEnLA0KICAgICcyMDIwLTA1LTI1JywnMjAyMC0wNi0xNScsJzIwMjAtMDYtMjInLCcyMDIwLTA2LTI5JywnMjAyMC0wNy0yMCcsJzIwMjAtMDgtMDcnLA0KICAgICcyMDIwLTA4LTE3JywnMjAyMC0xMC0xMicsJzIwMjAtMTEtMDInLCcyMDIwLTExLTE2JywnMjAyMC0xMi0wOCcsJzIwMjAtMTItMjUnDQogICAgKSkNCg0KZGYgPC0gZGYgJT4lIHNlbGVjdChDT01VTkEsIENMQVNFLCBGRUNIQSwgUEVSSU9ETywgTUVTLCBESUFfTk9NQlJFKQ0KDQpkZiRGRUNIQSA8LSBhcy5EYXRlKGRmJEZFQ0hBKQ0KDQojIFNlIGFncmVnYSBsYSB2YXJpYWJsZSBUSUVNUE8geSBNRVNfTk9NQlJFDQpkZiRNRVNfTk9NQlJFIDwtIHBhc3RlKGRmJFBFUklPRE8sIGRmJE1FUywgc2VwPSItIikgJT4lIGFzLnllYXJtb24oIiVZLSVtIikNCg0KIyBQYXJhIG9idGVuZXIgbGEgaW52ZXJzYSBzZSB1c2Fyw61hOiB6b286OmFzLkRhdGUoZGYkRkVDSEEsIG9yaWdpbj0iMjAxNC0wMS0wMSIpDQpkZiRUSUVNUE9fRElBIDwtIGFzLm51bWVyaWMoYXMuRGF0ZShkZiRGRUNIQSkpIC0gYXMubnVtZXJpYyhhcy5EYXRlKCIyMDE0LTAxLTAxIikpICsgMQ0KDQojIFNlIGNyZWEgbGEgdmFyaWFibGUgU0VNQU5BDQpkZiA8LSBkZiAlPiUgbXV0YXRlKFNFTUFOQSA9IHN0cmZ0aW1lKEZFQ0hBLCBmb3JtYXQgPSAiJVktJVYiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgVElFTVBPX1NFTUFOQSA9IG1hdGNoKFNFTUFOQSwgc29ydCh1bmlxdWUoU0VNQU5BKSkpKQ0KDQojIFNlIGNyZWEgbGEgdmFyaWFibGUgTUVTDQpkZiA8LSBkZiAlPiUgbXV0YXRlKE1FUyA9IHN0cmZ0aW1lKEZFQ0hBLCBmb3JtYXQgPSAiJVktJW0iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgVElFTVBPX01FUyA9IG1hdGNoKE1FUywgc29ydCh1bmlxdWUoTUVTKSkpKQ0KDQojIGTDrWFzIGZlc3Rpdm9zDQpkZiA8LSBkZiAlPiUgbXV0YXRlKERJQV9GRVNUSVZPID0gaWZlbHNlKHltZChGRUNIQSkgJWluJSBmZXN0aXZvcywxLDApKQ0KDQojIGFjY2lkZW50YWxpZGFkDQpkZiA8LSBkZiAlPiUgbXV0YXRlKEFDQ0lERU5UQUxJREFEID0gMSkNCmBgYA0KDQoNCiMjIE1vZGVsb3MgcHJlZGljdGl2b3MgcGFyYSBsYXMgQ29tdW5hcw0KDQpQYXJhIG9idGVuZXIgbGEgYmFzZSBkYXRvcyBlbiBsYSBjdWFsIHNlIGVuY3VlbnRyZSBlbCBuw7ptZXJvIGRlIGFjY2lkZW50ZXMgcGFyYSBjYWRhIGV2ZW50byBjb25zaWRlcmFkbywgc2UgY3JlYSB1bmEgYmFzZSBkZSBkYXRvcyBjb24gdG9kb3MgbG9zIGV2ZW50b3MgcG9zaWJsZXMgY29uIGF5dWRhIGRlIGxhIGZ1bmNpw7NuIGBleHBhbmQuZ3JpZGAsIGVuIGxhIGN1YWwgc2UgdGllbmUgZW4gY3VlbnRhIHRvZGFzIGxhIGNvbWJpbmFjaW9uZXMgZGUgY29tdW5hLCBjbGFzZSAodGlwbyBkZSBhY2NpZGVudGUpIHkgZmVjaGEuDQoNCmBgYHtyLCBldmFsID0gRn0NCmZlY2hhX3ZlY3RvciA8LSBhcy5EYXRlKGFzLkRhdGUoIjIwMTQtMDEtMDEiKTphcy5EYXRlKCIyMDE4LTEyLTMxIikpDQpiYXNlIDwtIGV4cGFuZC5ncmlkKENPTVVOQSA9IGxldmVscyhkZiRDT01VTkEpLCBDTEFTRSA9IGxldmVscyhkZiRDTEFTRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZFQ0hBID0gZmVjaGFfdmVjdG9yKQ0KYmFzZSA8LSBiYXNlICU+JSBtdXRhdGUoVElFTVBPX0RJQSA9IGFzLm51bWVyaWMoRkVDSEEpIC0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMubnVtZXJpYyhhcy5EYXRlKCIyMDE0LTAxLTAxIikpICsgMSkNCg0KIyBQRVJJT0RPDQpiYXNlIDwtIGJhc2UgJT4lIG11dGF0ZShQRVJJT0RPID0gYXMubnVtZXJpYyhmb3JtYXQoRkVDSEEsJyVZJykpKQ0KDQojIFNlIGNyZWEgbGEgdmFyaWFibGUgU0VNQU5BDQpiYXNlIDwtIGJhc2UgJT4lIG11dGF0ZShTRU1BTkEgPSBzdHJmdGltZShGRUNIQSwgZm9ybWF0ID0gIiVZLSVWIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIFRJRU1QT19TRU1BTkEgPSBtYXRjaChTRU1BTkEsIHNvcnQodW5pcXVlKFNFTUFOQSkpKSkNCg0KIyBTZSBjcmVhIGxhIHZhcmlhYmxlIE1FUw0KYmFzZSA8LSBiYXNlICU+JSBtdXRhdGUoTUVTID0gc3RyZnRpbWUoRkVDSEEsIGZvcm1hdCA9ICIlWS0lbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBUSUVNUE9fTUVTID0gbWF0Y2goTUVTLCBzb3J0KHVuaXF1ZShNRVMpKSkpDQoNCiMgZMOtYXMgZmVzdGl2b3MNCmJhc2UgPC0gYmFzZSAlPiUgbXV0YXRlKERJQV9GRVNUSVZPID0gaWZlbHNlKHltZChGRUNIQSkgJWluJSBmZXN0aXZvcywxLDApKQ0KYGBgDQoNCkx1ZWZvIHNlIHJlYWxpemEgdW4gKmxlZnQgam9pbiogY29uIGxhIGJhc2UgcXVlIGNvbnRpZW5lIHRvZG9zIGxvcyBwb3NpYmxlcyBldmVudG9zIHkgbGEgYmFzZSBkZSBkYXRvcyBxdWUgY29udGllbmUgbGEgaW5mb3JtYWNpw7NuIGRlIGxvcyBhY2NpZGVudGVzIChiYXNlIGRlcHVyYWRhKS4NCg0KYGBge3IsIGV2YWwgPSBGfQ0KYmFzZSA8LSBsZWZ0X2pvaW4oYmFzZSwgc3Vic2V0KGRmLCBzZWxlY3QgPSAtRElBX05PTUJSRSksDQogICAgICAgICAgICAgICAgICBieSA9IGMoIkNPTVVOQSIsICJDTEFTRSIsICJGRUNIQSIsICJUSUVNUE9fRElBIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQRVJJT0RPIiwgIlNFTUFOQSIsICJUSUVNUE9fU0VNQU5BIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUSUVNUE9fTUVTIiwgIkRJQV9GRVNUSVZPIikpDQpiYXNlW2lzLm5hKGJhc2UpXSA8LSAwDQoNCmFjY2lkZW50ZXNfZGlhX2NvbXVuYSA8LSBsZWZ0X2pvaW4oYmFzZSwgZGlzdGluY3QoZGZbLCBjKCJGRUNIQSIsICJESUFfTk9NQlJFIildKSwgYnkgPSAiRkVDSEEiKQ0KYGBgDQoNCiMjIyBNb2RlbG9zIC0gdW5pZGFkIGRlIHRpZW1wbyBEw61hDQoNCkxhIGJhc2UgZGUgZGF0b3MgY29uIGVsIG7Dum1lcm8gZGUgYWNjaWRlbnRlcyBwb3IgY29tdW5hLCBjbGFzZSB5IGTDrWEgc2Ugb2J0dXZvIGFudGVyaW9ybWVudGUsIMOpc3RhIGNvbnRpZW5lIDE3NTI5NiBvYnNlcnZhY2lvbmVzLg0KDQpgYGB7ciwgaW5jbHVkZSA9IEZ9DQpsb2FkKGZpbGUgPSAiYWNjaWRlbnRlc19kaWFfY29tdW5hLlJEYXRhIikgIyBiYXNlIGRlIGRhdG9zDQpgYGANCg0KYGBge3J9DQpkaW0oYWNjaWRlbnRlc19kaWFfY29tdW5hKQ0KYGBgDQoNCkxhIGJhc2UgbHVjZSBkZSBsYSBzaWd1aWVudGUgbWFuZXJhOg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgcm93cy5wcmludCA9IDV9DQphY2NpZGVudGVzX2RpYV9jb211bmEgPC0gc3Vic2V0KGFjY2lkZW50ZXNfZGlhX2NvbXVuYSwgc2VsZWN0PWMoMSwyLDMsMTIsNCw1LDYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNyw4LDksMTAsMTEpKQ0KYWNjaWRlbnRlc19kaWFfY29tdW5hDQpgYGANCg0KU2UgcmVhbGl6YSBsYSBwYXJ0aWNpw7NuIGRlIGxvcyBkYXRvcyBwYXJhIHNlbGVjY2lvbmFyIGVsIGNvbmp1bnRvIGRlIGVudHJlbmFtaWVudG8geSBlbCBjb25qdW50byBkZSBwcnVlYmEuDQoNCmBgYHtyfQ0KdGVzdF9kaWFfY29tdW5hIDwtIGFjY2lkZW50ZXNfZGlhX2NvbXVuYVthY2NpZGVudGVzX2RpYV9jb211bmEkUEVSSU9ETyA9PSAyMDE4LCBdDQp0cmFpbl9kaWFfY29tdW5hIDwtIGFjY2lkZW50ZXNfZGlhX2NvbXVuYVthY2NpZGVudGVzX2RpYV9jb211bmEkUEVSSU9ETyAlaW4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMjAxNCwgMjAxNSwgMjAxNiwgMjAxNyksIF0NCmBgYA0KDQpTZSBwcm9jZWRlIGEgbGEgY3JlYWNpw7NuIGRlIG1vZGVsb3M6DQoNClNlIGNvbnNpZGVyYW4gZGlzdGludG9zIG1vZGVsb3MgcGFydGllbmRvIGRlIHVuIG1vZGVsbyBqZXLDoXJxdWljbyBjb24gMiBuaXZlbGVzIChDTEFTRSB5IENPTVVOQSkgY29uIHNvbG8gdW4gaW50ZXJjZXB0bywgYW1ib3Mgbml2ZWxlcyBvIHZhcmlhYmxlcyBjb24gZWZlY3RvcyBhbGVhdG9yaW9zIHNlIGNvbnNpZGVyYW4gY29ycmVsYWNpb25hZG9zIHkgc2UgcG9uZHLDoSBsYSB2YXJpYWJsZSBDTEFTRSAodGlwbyBkZSBhY2NpZGVudGUpIGRlbnRybyBkZSBsYSB2YXJpYWJsZSBjb211bmEuDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KbG9hZChmaWxlID0gIm1vZF9kaWFfY29tdW5hMC5SRGF0YSIpDQpsb2FkKGZpbGUgPSAibW9kX2RpYV9jb211bmExLlJEYXRhIikNCmxvYWQoZmlsZSA9ICJtb2RfZGlhX2NvbXVuYTIuUkRhdGEiKQ0KbG9hZChmaWxlID0gIm1vZF9kaWFfY29tdW5hMy5SRGF0YSIpDQpsb2FkKGZpbGUgPSAibW9kX2RpYV9jb211bmE0LlJEYXRhIikNCmBgYA0KDQpgYGB7ciwgZXZhbCA9IEZBTFNFfQ0KbW9kX2RpYV9jb211bmEwIDwtIGdsbWVyKEFDQ0lERU5UQUxJREFEIH4gMSArICgxIHwgQ09NVU5BL0NMQVNFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGlhX2NvbXVuYSwgZmFtaWx5PSBwb2lzc29uKCkpDQptb2RfZGlhX2NvbXVuYTEgPC0gZ2xtZXIoQUNDSURFTlRBTElEQUQgfiBESUFfRkVTVElWTyArICgxIHwgQ09NVU5BL0NMQVNFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGlhX2NvbXVuYSwgZmFtaWx5PSBwb2lzc29uKCkpDQptb2RfZGlhX2NvbXVuYTIgPC0gZ2xtZXIoQUNDSURFTlRBTElEQUQgfiBESUFfRkVTVElWTyArIFRJRU1QT19ESUEgKyAoMSB8IENPTVVOQS9DTEFTRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX2RpYV9jb211bmEsIGZhbWlseT0gcG9pc3NvbigpKQ0KbW9kX2RpYV9jb211bmEzIDwtIGdsbWVyKEFDQ0lERU5UQUxJREFEIH4gRElBX0ZFU1RJVk8gKyBESUFfTk9NQlJFICsgKDEgfCBDT01VTkEvQ0xBU0UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kaWFfY29tdW5hLCBmYW1pbHk9IHBvaXNzb24oKSkNCm1vZF9kaWFfY29tdW5hNCA8LSBnbG1lcihBQ0NJREVOVEFMSURBRCB+IERJQV9GRVNUSVZPICsgRElBX05PTUJSRSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAoMSArIERJQV9GRVNUSVZPfCBDT01VTkEvQ0xBU0UpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kaWFfY29tdW5hLCBmYW1pbHk9IHBvaXNzb24oKSkNCmBgYA0KDQpTZSBjb21wYXJhbiBsb3MgbW9kZWxvcyBwb3IgdW4gYW5hbMOtc2lzIGRlIHZhcmlhbnphIEFOT1ZBOg0KDQpgYGB7cn0NCmFub3ZhKG1vZF9kaWFfY29tdW5hMCwgbW9kX2RpYV9jb211bmExLCBtb2RfZGlhX2NvbXVuYTIsIG1vZF9kaWFfY29tdW5hMywgbW9kX2RpYV9jb211bmE0KQ0KYGBgDQoNCkFsIHBhcmVjZXIgY2FkYSBtb2RlbG8gZXhwbGljYSBtZWpvciBsYSBhY2NpZGVudGFsaWRhZCBxdWUgZWwgYW50ZXJpb3IsIHBlcm8gc2UgZGViZSByZWNvcmRhciBxdWUgZWwgKlZhbG9yLVAqIGVzIHNlbnNpYmxlIGFsIG7Dum1lcm8gZGUgZGF0b3MuIEVuIGxhIGJhc2UgZGUgZW50cmVuYW1pZW50byBoYXkgMTQwMjU2IG9ic2VydmFjaW9uZXMsIGVzdG8gc2UgY29uc2lkZXJhIGdyYW5kZSwgcG9yIGxvIHF1ZSBubyBoYXkgcXVlIGZpamFyc2UgbXVjaG8gZW4gZWwgKlZhbG9yLVAqIGRhZG8gcXVlIGVzIG3DoXMgZsOhY2lsIHF1ZSByZWNoYWNlIGxhcyBoaXDDs3Rlc2lzIG51bGFzLg0KDQpTZSBwcm9jZWRlIGEgY2FsY3VsYXIgZWwgTVNFIGRlIGVudHJlbmFtaWVudG8geSBkZSBwcnVlYmEgZW4gY2FkYSB1bm8gZGUgbG9zIG1vZGVsb3MsIGNhYmUgcmVzYWx0YXIgcXVlIGxvcyB2YWxvcmVzIHByZWRpY2hvcyBzZSByZWRvbmRlYXLDoW4gYWwgZW50ZXJvIG3DoXMgY2VyY2FubyBkZWJpZG8gYSBxdWUgc29uIGRhdG9zIGRlIGNvbnRlbyB5IGxhIGZ1bmNpw7NuIGBwcmVkaWN0YCBkZXZ1ZWx2ZSB2YWxvcmVzIGVuIGxhIGVzY2FsYSBvcmlnaW5hbCBwZXJvIGNvbiBkw6ljaW1hbGVzLg0KDQpgYGB7cn0NCiMgTW9kZWxvIDANCnlfZXN0X3RyYWluX2RpYV9jb211bmEwIDwtIHJvdW5kKHByZWRpY3QobW9kX2RpYV9jb211bmEwLCBuZXdkYXRhID0gdHJhaW5fZGlhX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQp5X2VzdF90ZXN0X2RpYV9jb211bmEwIDwtIHJvdW5kKHByZWRpY3QobW9kX2RpYV9jb211bmEwLCBuZXdkYXRhID0gdGVzdF9kaWFfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KbXNlX3RyYWluX2RpYV9jb211bmEwIDwtIHJvdW5kKE1TRSh0cmFpbl9kaWFfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90cmFpbl9kaWFfY29tdW5hMCksNCkNCm1zZV90ZXN0X2RpYV9jb211bmEwIDwtIHJvdW5kKE1TRSh0ZXN0X2RpYV9jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90ZXN0X2RpYV9jb211bmEwKSw0KQ0KDQojIE1vZGVsbyAxDQp5X2VzdF90cmFpbl9kaWFfY29tdW5hMSA8LSByb3VuZChwcmVkaWN0KG1vZF9kaWFfY29tdW5hMSwgbmV3ZGF0YSA9IHRyYWluX2RpYV9jb211bmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KeV9lc3RfdGVzdF9kaWFfY29tdW5hMSA8LSByb3VuZChwcmVkaWN0KG1vZF9kaWFfY29tdW5hMSwgbmV3ZGF0YSA9IHRlc3RfZGlhX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIiksMCkNCm1zZV90cmFpbl9kaWFfY29tdW5hMSA8LSByb3VuZChNU0UodHJhaW5fZGlhX2NvbXVuYSRBQ0NJREVOVEFMSURBRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9lc3RfdHJhaW5fZGlhX2NvbXVuYTEpLDQpDQptc2VfdGVzdF9kaWFfY29tdW5hMSA8LSByb3VuZChNU0UodGVzdF9kaWFfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfZXN0X3Rlc3RfZGlhX2NvbXVuYTEpLDQpDQoNCiMgTW9kZWxvIDINCnlfZXN0X3RyYWluX2RpYV9jb211bmEyIDwtIHJvdW5kKHByZWRpY3QobW9kX2RpYV9jb211bmEyLCBuZXdkYXRhID0gdHJhaW5fZGlhX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQp5X2VzdF90ZXN0X2RpYV9jb211bmEyIDwtIHJvdW5kKHByZWRpY3QobW9kX2RpYV9jb211bmEyLCBuZXdkYXRhID0gdGVzdF9kaWFfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KbXNlX3RyYWluX2RpYV9jb211bmEyIDwtIHJvdW5kKE1TRSh0cmFpbl9kaWFfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90cmFpbl9kaWFfY29tdW5hMiksNCkNCm1zZV90ZXN0X2RpYV9jb211bmEyIDwtIHJvdW5kKE1TRSh0ZXN0X2RpYV9jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9lc3RfdGVzdF9kaWFfY29tdW5hMiksNCkNCg0KIyBNb2RlbG8gMw0KeV9lc3RfdHJhaW5fZGlhX2NvbXVuYTMgPC0gcm91bmQocHJlZGljdChtb2RfZGlhX2NvbXVuYTMsIG5ld2RhdGEgPSB0cmFpbl9kaWFfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIiksMCkNCnlfZXN0X3Rlc3RfZGlhX2NvbXVuYTMgPC0gcm91bmQocHJlZGljdChtb2RfZGlhX2NvbXVuYTMsIG5ld2RhdGEgPSB0ZXN0X2RpYV9jb211bmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQptc2VfdHJhaW5fZGlhX2NvbXVuYTMgPC0gcm91bmQoTVNFKHRyYWluX2RpYV9jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfZXN0X3RyYWluX2RpYV9jb211bmEzKSw0KQ0KbXNlX3Rlc3RfZGlhX2NvbXVuYTMgPC0gcm91bmQoTVNFKHRlc3RfZGlhX2NvbXVuYSRBQ0NJREVOVEFMSURBRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90ZXN0X2RpYV9jb211bmEzKSw0KQ0KDQojIE1vZGVsbyA0DQp5X2VzdF90cmFpbl9kaWFfY29tdW5hNCA8LSByb3VuZChwcmVkaWN0KG1vZF9kaWFfY29tdW5hNCwgbmV3ZGF0YSA9IHRyYWluX2RpYV9jb211bmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KeV9lc3RfdGVzdF9kaWFfY29tdW5hNCA8LSByb3VuZChwcmVkaWN0KG1vZF9kaWFfY29tdW5hNCwgbmV3ZGF0YSA9IHRlc3RfZGlhX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIiksMCkNCm1zZV90cmFpbl9kaWFfY29tdW5hNCA8LSByb3VuZChNU0UodHJhaW5fZGlhX2NvbXVuYSRBQ0NJREVOVEFMSURBRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9lc3RfdHJhaW5fZGlhX2NvbXVuYTQpLDQpDQptc2VfdGVzdF9kaWFfY29tdW5hNCA8LSByb3VuZChNU0UodGVzdF9kaWFfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfZXN0X3Rlc3RfZGlhX2NvbXVuYTQpLDQpDQoNCmBgYA0KDQoNCmBgYHtyLCBpbmNsdWRlPUZ9DQpkYXRvc19tc2UgPC0gZGF0YS5mcmFtZShNb2RlbG9fTWl4dG9fUG9pc3NvbiA9IGMoIkFDQ0lERU5UQUxJREFEIH4gMSArICgxIHwgQ09NVU5BL0NMQVNFKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNDSURFTlRBTElEQUQgfiBESUFfRkVTVElWTyArICgxIHwgQ09NVU5BL0NMQVNFKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNDSURFTlRBTElEQUQgfiBESUFfRkVTVElWTyArIFRJRU1QT19ESUEgKyAoMSB8IENPTVVOQS9DTEFTRSkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFDQ0lERU5UQUxJREFEIH4gRElBX0ZFU1RJVk8gKyBESUFfTk9NQlJFICsgKDEgfCBDT01VTkEvQ0xBU0UpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBQ0NJREVOVEFMSURBRCB+IERJQV9GRVNUSVZPICsgRElBX05PTUJSRSArICgxICsgRElBX0ZFU1RJVk8gfCBDT01VTkEvQ0xBU0UpIiksDQogICAgICAgICAgICAgICAgICAgICAgICBUcmFpbl9NU0UgPSBjKG1zZV90cmFpbl9kaWFfY29tdW5hMCwgbXNlX3RyYWluX2RpYV9jb211bmExLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtc2VfdHJhaW5fZGlhX2NvbXVuYTIsIG1zZV90cmFpbl9kaWFfY29tdW5hMywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXNlX3RyYWluX2RpYV9jb211bmE0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgIFRlc3RfTVNFID0gYyhtc2VfdGVzdF9kaWFfY29tdW5hMCwgbXNlX3Rlc3RfZGlhX2NvbXVuYTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXNlX3Rlc3RfZGlhX2NvbXVuYTIsIG1zZV90ZXN0X2RpYV9jb211bmEzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1zZV90ZXN0X2RpYV9jb211bmE0KSkNCmRhdG9zX21zZSA8LSBkYXRvc19tc2UgJT4lDQogIG11dGF0ZShQb3JjZW50YWplX1ZhcmlhY2lvbiA9IHJvdW5kKDEwMCphYnMoVHJhaW5fTVNFLVRlc3RfTVNFKS8oVHJhaW5fTVNFK1Rlc3RfTVNFKSwgMikpDQpgYGANCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjprYWJsZShkYXRvc19tc2UpDQpgYGANCg0KRW4gbG9zIG1vZGVsb3MgKm1vZF9kaWFfY29tdW5hMyogeSAqbW9kX2RpYV9jb211bmE0KiBzZSBkZXNjYXJ0YSB1c2FyIGxhIHZhcmlhYmxlIFRJRU1QT19ESUEgZGViaWRvIGEgcXVlIGVuIGx1Z2FyIGVuIGRpc21pbnVpciBlbCBUZXN0X01TRSwgbG8gYXVtZW50w7MsIHkgdGFtcG9jbyBtZWpvcsOzIGVsIFRyYWluX01TRSBzaWduaWZpY2F0aXZhbWVudGUsIHBvciBsbyBxdWUgc2UgY29uc2lkZXJhIHF1ZSBubyBlcyBzaWduaWZpY2F0aXZhIHBhcmEgZXhwbGljYXIgbGEgYWNjaWRlbnRhbGlkYWQgZW4gbGFzIGNvbXVuYXMgZGUgTWVkZWxsw61uIHBvciB0aXBvIGRlIGFjY2lkZW50ZS4NCg0KRmluYWxtZW50ZSBzZSBvYnNlcnZhIHF1ZSBlbCBtb2RlbG8gY29uIGludGVyY2VwdG8geSBwZW5kaWVudGUgYWxlYXRvcmlhICoqbW9kX2RpYV9jb211bmE0Kio6DQoNCiQkXHdpZGVoYXR7XHRleHR7QUNDSURFTlRBTElEQUR9fSA9IFx0ZXh0e0RJQV9GRVNUSVZPfSArIFx0ZXh0e0RJQV9OT01CUkV9ICsgKDEgKyBcdGV4dHtESUFfRkVTVElWT30gfCBcdGV4dHtDT01VTkEvQ0xBU0V9KSQkDQoNCkVzIGVsIHF1ZSBvYnRpZW5lIGVsIG1lbm9yICpUcmFpbl9NU0UqIHkgKlRlc3RfTVNFKiwgcG9yIGxvIHF1ZSBzZSBlc2NvZ2UgZXN0ZSBtb2RlbG8gcGFyYSBsYSBleHBsaWNhY2nDs24gZGUgbGEgYWNjaWRlbnRhbGlkYWQgZW4gY29tdW5hIHBvciBkw61hLg0KDQpBIGNvbnRpbnVhY2nDs24gc2UgbXVlc3RyYW4gYWxndW5vcyBkZSBsb3MgdmFsb3JlcyBvYnNlcnZhZG9zIGRlIGxhIGFjY2lkZW50YWxpZGFkIGRpYXJpYSBwb3IgY2xhc2UgeSBwb3IgY29tdW5hLCBjb24gc3UgcmVzcGVjdGl2byB2YWxvciBwcmVkaWNobyDDumx0aW1hIGNvbHVtbmEpIGVuIGVsIGNvbmp1bnRvIGRlIHBydWViYS4NCg0KYGBge3IsIHJvd3MucHJpbnQgPSA1LCBlY2hvID0gRn0NCmRhdGEuZnJhbWUodGVzdF9kaWFfY29tdW5hLCBBLlBSRURJQ0hBID0geV9lc3RfdGVzdF9kaWFfY29tdW5hNCkNCmBgYA0KDQpgYGB7ciwgaW5jbHVkZT1GfQ0KcmVtb3ZlKHlfZXN0X3RyYWluX2RpYV9jb211bmEwLCB5X2VzdF90cmFpbl9kaWFfY29tdW5hMSwgeV9lc3RfdHJhaW5fZGlhX2NvbXVuYTIsIHlfZXN0X3RyYWluX2RpYV9jb211bmEzLCB5X2VzdF90cmFpbl9kaWFfY29tdW5hNCwgeV9lc3RfdGVzdF9kaWFfY29tdW5hMCwgeV9lc3RfdGVzdF9kaWFfY29tdW5hMSwgeV9lc3RfdGVzdF9kaWFfY29tdW5hMiwgeV9lc3RfdGVzdF9kaWFfY29tdW5hMywgeV9lc3RfdGVzdF9kaWFfY29tdW5hNCxtc2VfdHJhaW5fZGlhX2NvbXVuYTAsbXNlX3RyYWluX2RpYV9jb211bmExLG1zZV90cmFpbl9kaWFfY29tdW5hMixtc2VfdHJhaW5fZGlhX2NvbXVuYTMsbXNlX3RyYWluX2RpYV9jb211bmE0LG1zZV90ZXN0X2RpYV9jb211bmEwLG1zZV90ZXN0X2RpYV9jb211bmExLG1zZV90ZXN0X2RpYV9jb211bmEyLG1zZV90ZXN0X2RpYV9jb211bmEzLG1zZV90ZXN0X2RpYV9jb211bmE0KQ0KYGBgDQoNCg0KIyMjIE1vZGVsb3MgLSB1bmlkYWQgZGUgdGllbXBvIFNlbWFuYQ0KDQpQYXJhIGVzdGUgbW9kZWxvIHNlIGFncnVwYSBsYSB1bmlkYWQgZGUgdGllbXBvIHBvciBzZW1hbmFzLCBlbiBsYXMgY3VhbGVzIHNlIHN1bWEgbGEgY2FudGlkYWQgZGUgYWNjaWRlbnRlcyBxdWUgc3VjZWRlbiBlbCBsYSBtaXNtYSwgdGFtYmnDqW4gc2Ugc3VtYSBlbCBuw7ptZXJvIGRlIGTDrWFzIGZlc3Rpdm9zLiBMYSBiYXNlIGx1Y2UgY29tbyBzaWd1ZToNCg0KYGBge3IsIHJvd3MucHJpbnQgPSA1fQ0KYWNjaWRlbnRlc19zZW1hbmFfY29tdW5hIDwtIGFjY2lkZW50ZXNfZGlhX2NvbXVuYSAlPiUNCiAgZ3JvdXBfYnkoQ09NVU5BLCBDTEFTRSwgUEVSSU9ETywgU0VNQU5BLCBUSUVNUE9fU0VNQU5BKSAlPiUNCiAgc3VtbWFyaXNlKERJQV9GRVNUSVZPID0gc3VtKERJQV9GRVNUSVZPKSwgQUNDSURFTlRBTElEQUQgPSBzdW0oQUNDSURFTlRBTElEQUQpKQ0KDQphY2NpZGVudGVzX3NlbWFuYV9jb211bmENCmBgYA0KDQoNClNlIHByb2NlZGUgYSByZWFsaXphciBsYSBwYXJ0aWNpw7NuIGRlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvIHkgZWwgY29uanVudG8gZGUgcHJ1ZWJhOg0KDQpgYGB7cn0NCnRlc3Rfc2VtYW5hX2NvbXVuYSA8LSBhY2NpZGVudGVzX3NlbWFuYV9jb211bmFbYWNjaWRlbnRlc19zZW1hbmFfY29tdW5hJFBFUklPRE8gPT0gMjAxOCwgXQ0KdHJhaW5fc2VtYW5hX2NvbXVuYSA8LSBhY2NpZGVudGVzX3NlbWFuYV9jb211bmFbYWNjaWRlbnRlc19zZW1hbmFfY29tdW5hJFBFUklPRE8gJWluJQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDIwMTQsIDIwMTUsIDIwMTYsIDIwMTcpLCBdDQpgYGANCg0KQWhvcmEgc2UgYWp1c3RhbiBsb3MgbW9kZWxvcyBtaXh0b3MgY29uIGVmZWN0b3MgYWxlYXRvcmlvcyBlbiBDT01VTkEgeSBDTEFTRSBhbCBpZ3VhbCBxdWUgc2UgaGl6byBhIG5pdmVsIGRpYXJpbywgZXMgZGVjaXIsIENMQVNFIGRlbnRybyBkZSBDT01VTkEuDQoNCmBgYHtyLCBlY2hvPUZ9DQpsb2FkKGZpbGUgPSAibW9kX3NlbWFuYV9jb211bmEwLlJEYXRhIikNCmxvYWQoZmlsZSA9ICJtb2Rfc2VtYW5hX2NvbXVuYTEuUkRhdGEiKQ0KbG9hZChmaWxlID0gIm1vZF9zZW1hbmFfY29tdW5hMi5SRGF0YSIpDQpsb2FkKGZpbGUgPSAibW9kX3NlbWFuYV9jb211bmEzLlJEYXRhIikNCmBgYA0KDQpgYGB7ciwgZXZhbCA9IEZ9DQptb2Rfc2VtYW5hX2NvbXVuYTAgPC0gZ2xtZXIoQUNDSURFTlRBTElEQUQgfiAgMSArICgxIHwgQ09NVU5BL0NMQVNFKSwNCiAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9zZW1hbmFfY29tdW5hLCBmYW1pbHk9IHBvaXNzb24oKSkNCm1vZF9zZW1hbmFfY29tdW5hMSA8LSBnbG1lcihBQ0NJREVOVEFMSURBRCB+ICBESUFfRkVTVElWTyArICgxIHwgQ09NVU5BL0NMQVNFKSwNCiAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9zZW1hbmFfY29tdW5hLCBmYW1pbHk9IHBvaXNzb24oKSkNCm1vZF9zZW1hbmFfY29tdW5hMiA8LSBnbG1lcihBQ0NJREVOVEFMSURBRCB+ICBESUFfRkVTVElWTyArIFRJRU1QT19TRU1BTkEgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKDEgfCBDT01VTkEvQ0xBU0UpLA0KICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX3NlbWFuYV9jb211bmEsIGZhbWlseT0gcG9pc3NvbigpKQ0KbW9kX3NlbWFuYV9jb211bmEzIDwtIGdsbWVyKEFDQ0lERU5UQUxJREFEIH4gIERJQV9GRVNUSVZPICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICgxICsgRElBX0ZFU1RJVk8gfCBDT01VTkEvQ0xBU0UpLA0KICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX3NlbWFuYV9jb211bmEsIGZhbWlseT0gcG9pc3NvbigpKQ0KYGBgDQoNClNlIGNvbXBhcmFuIGxvcyBtb2RlbG9zIHBvciB1biBhbmFsw61zaXMgZGUgdmFyaWFuemEgQU5PVkE6DQoNCmBgYHtyfQ0KYW5vdmEobW9kX3NlbWFuYV9jb211bmEwLCBtb2Rfc2VtYW5hX2NvbXVuYTEsIG1vZF9zZW1hbmFfY29tdW5hMiwgbW9kX3NlbWFuYV9jb211bmEzKQ0KYGBgDQoNClNlIHRpZW5lIGVsIG1pc21vIHByb2JsZW1hIGRlbCB0YW1hw7FvIGRlIG11ZXN0cmEgcXVlIGVuIGVsIG1vZGVsbyBkaWFyaW8uIEFob3JhLCBzZSBwcm9jZWRlIGEgY2FsY3VsYXIgZWwgTVNFIGRlIGVudHJlbmFtaWVudG8geSBkZSBwcnVlYmEgZW4gY2FkYSB1bm8gZGUgbG9zIG1vZGVsb3MsIGNhYmUgcmVzYWx0YXIgcXVlIGxvcyB2YWxvcmVzIHByZWRpY2hvcyBzZSByZWRvbmRlYXLDoW4gYWwgZW50ZXJvIG3DoXMgY2VyY2FubyBkZWJpZG8gYSBxdWUgc29uIGRhdG9zIGRlIGNvbnRlbyB5IGxhIGZ1bmNpw7NuIGBwcmVkaWN0YCBkZXZ1ZWx2ZSB2YWxvcmVzIGVuIGxhIGVzY2FsYSBvcmlnaW5hbCBwZXJvIGNvbiBkw6ljaW1hbGVzLg0KDQpgYGB7cn0NCiMgTW9kZWxvIDANCnlfZXN0X3RyYWluX3NlbWFuYV9jb211bmEwIDwtIHJvdW5kKHByZWRpY3QobW9kX3NlbWFuYV9jb211bmEwLCBuZXdkYXRhID0gdHJhaW5fc2VtYW5hX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQp5X2VzdF90ZXN0X3NlbWFuYV9jb211bmEwIDwtIHJvdW5kKHByZWRpY3QobW9kX3NlbWFuYV9jb211bmEwLCBuZXdkYXRhID0gdGVzdF9zZW1hbmFfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KbXNlX3RyYWluX3NlbWFuYV9jb211bmEwIDwtIHJvdW5kKE1TRSh0cmFpbl9zZW1hbmFfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90cmFpbl9zZW1hbmFfY29tdW5hMCksNCkNCm1zZV90ZXN0X3NlbWFuYV9jb211bmEwIDwtIHJvdW5kKE1TRSh0ZXN0X3NlbWFuYV9jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90ZXN0X3NlbWFuYV9jb211bmEwKSw0KQ0KDQojIE1vZGVsbyAxDQp5X2VzdF90cmFpbl9zZW1hbmFfY29tdW5hMSA8LSByb3VuZChwcmVkaWN0KG1vZF9zZW1hbmFfY29tdW5hMSwgbmV3ZGF0YSA9IHRyYWluX3NlbWFuYV9jb211bmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KeV9lc3RfdGVzdF9zZW1hbmFfY29tdW5hMSA8LSByb3VuZChwcmVkaWN0KG1vZF9zZW1hbmFfY29tdW5hMSwgbmV3ZGF0YSA9IHRlc3Rfc2VtYW5hX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIiksMCkNCm1zZV90cmFpbl9zZW1hbmFfY29tdW5hMSA8LSByb3VuZChNU0UodHJhaW5fc2VtYW5hX2NvbXVuYSRBQ0NJREVOVEFMSURBRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9lc3RfdHJhaW5fc2VtYW5hX2NvbXVuYTEpLDQpDQptc2VfdGVzdF9zZW1hbmFfY29tdW5hMSA8LSByb3VuZChNU0UodGVzdF9zZW1hbmFfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfZXN0X3Rlc3Rfc2VtYW5hX2NvbXVuYTEpLDQpDQoNCiMgTW9kZWxvIDINCnlfZXN0X3RyYWluX3NlbWFuYV9jb211bmEyIDwtIHJvdW5kKHByZWRpY3QobW9kX3NlbWFuYV9jb211bmEyLCBuZXdkYXRhID0gdHJhaW5fc2VtYW5hX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQp5X2VzdF90ZXN0X3NlbWFuYV9jb211bmEyIDwtIHJvdW5kKHByZWRpY3QobW9kX3NlbWFuYV9jb211bmEyLCBuZXdkYXRhID0gdGVzdF9zZW1hbmFfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KbXNlX3RyYWluX3NlbWFuYV9jb211bmEyIDwtIHJvdW5kKE1TRSh0cmFpbl9zZW1hbmFfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90cmFpbl9zZW1hbmFfY29tdW5hMiksNCkNCm1zZV90ZXN0X3NlbWFuYV9jb211bmEyIDwtIHJvdW5kKE1TRSh0ZXN0X3NlbWFuYV9jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9lc3RfdGVzdF9zZW1hbmFfY29tdW5hMiksNCkNCg0KIyBNb2RlbG8gMw0KeV9lc3RfdHJhaW5fc2VtYW5hX2NvbXVuYTMgPC0gcm91bmQocHJlZGljdChtb2Rfc2VtYW5hX2NvbXVuYTMsIG5ld2RhdGEgPSB0cmFpbl9zZW1hbmFfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIiksMCkNCnlfZXN0X3Rlc3Rfc2VtYW5hX2NvbXVuYTMgPC0gcm91bmQocHJlZGljdChtb2Rfc2VtYW5hX2NvbXVuYTMsIG5ld2RhdGEgPSB0ZXN0X3NlbWFuYV9jb211bmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQptc2VfdHJhaW5fc2VtYW5hX2NvbXVuYTMgPC0gcm91bmQoTVNFKHRyYWluX3NlbWFuYV9jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfZXN0X3RyYWluX3NlbWFuYV9jb211bmEzKSw0KQ0KbXNlX3Rlc3Rfc2VtYW5hX2NvbXVuYTMgPC0gcm91bmQoTVNFKHRlc3Rfc2VtYW5hX2NvbXVuYSRBQ0NJREVOVEFMSURBRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90ZXN0X3NlbWFuYV9jb211bmEzKSw0KQ0KYGBgDQoNCg0KDQpgYGB7ciwgaW5jbHVkZT1GfQ0KZGF0b3NfbXNlIDwtIGRhdGEuZnJhbWUoTW9kZWxvX01peHRvX1BvaXNzb24gPSBjKCJBQ0NJREVOVEFMSURBRCB+IDEgKyAoMSB8IENPTVVOQS9DTEFTRSkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFDQ0lERU5UQUxJREFEIH4gRElBX0ZFU1RJVk8gKyAoMSB8IENPTVVOQS9DTEFTRSkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFDQ0lERU5UQUxJREFEIH4gRElBX0ZFU1RJVk8gKyBUSUVNUE9fU0VNQU5BICsgKDEgfCBDT01VTkEvQ0xBU0UpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBQ0NJREVOVEFMSURBRCB+IERJQV9GRVNUSVZPICsgKDEgKyBESUFfRkVTVElWTyB8IENPTVVOQS9DTEFTRSkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIFRyYWluX01TRSA9IGMobXNlX3RyYWluX3NlbWFuYV9jb211bmEwLCBtc2VfdHJhaW5fc2VtYW5hX2NvbXVuYTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1zZV90cmFpbl9zZW1hbmFfY29tdW5hMiwgbXNlX3RyYWluX3NlbWFuYV9jb211bmEzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIFRlc3RfTVNFID0gYyhtc2VfdGVzdF9zZW1hbmFfY29tdW5hMCwgbXNlX3Rlc3Rfc2VtYW5hX2NvbXVuYTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXNlX3Rlc3Rfc2VtYW5hX2NvbXVuYTIsIG1zZV90ZXN0X3NlbWFuYV9jb211bmEzKSkNCmRhdG9zX21zZSA8LSBkYXRvc19tc2UgJT4lDQogIG11dGF0ZShQb3JjZW50YWplX1ZhcmlhY2lvbiA9IHJvdW5kKDEwMCphYnMoVHJhaW5fTVNFLVRlc3RfTVNFKS8oVHJhaW5fTVNFK1Rlc3RfTVNFKSwgMikpDQpgYGANCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjprYWJsZShkYXRvc19tc2UpDQpgYGANCg0KRW4gZWwgbW9kZWxvICptb2Rfc2VtYW5hX2NvbXVuYTMqIHNlIGRlc2NhcnRhIHVzYXIgbGEgdmFyaWFibGUgVElFTVBPX1NFTUFOQSBkZWJpZG8gYSBxdWUgZW4gbHVnYXIgZW4gZGlzbWludWlyIGVsIFRlc3RfTVNFLCBsbyBhdW1lbnTDsywgeSB0YW1wb2NvIG1lam9yw7MgZWwgVHJhaW5fTVNFIHNpZ25pZmljYXRpdmFtZW50ZSwgcG9yIGxvIHF1ZSBzZSBjb25zaWRlcmEgcXVlIG5vIGVzIHNpZ25pZmljYXRpdmEgcGFyYSBleHBsaWNhciBsYSBhY2NpZGVudGFsaWRhZCBlbiBsYXMgY29tdW5hcyBkZSBNZWRlbGzDrW4gcG9yIHRpcG8gZGUgYWNjaWRlbnRlLg0KDQpGaW5hbG1lbnRlIHNlIG9ic2VydmEgcXVlIGVsIG1vZGVsbyBjb24gaW50ZXJjZXB0byB5IHBlbmRpZW50ZSBhbGVhdG9yaWEgKiptb2Rfc2VtYW5hX2NvbXVuYTMqKjoNCg0KJCRcd2lkZWhhdHtcdGV4dHtBQ0NJREVOVEFMSURBRH19ID0gXHRleHR7RElBX0ZFU1RJVk99ICsgKDEgKyBcdGV4dHtESUFfRkVTVElWT30gfCBcdGV4dHtDT01VTkEvQ0xBU0V9KSQkDQoNCkVzIGVsIHF1ZSBvYnRpZW5lIGVsIG1lbm9yICpUcmFpbl9NU0UqIHkgKlRlc3RfTVNFKiwgcG9yIGxvIHF1ZSBzZSBlc2NvZ2UgZXN0ZSBtb2RlbG8gcGFyYSBsYSBleHBsaWNhY2nDs24gZGUgbGEgYWNjaWRlbnRhbGlkYWQgZW4gY29tdW5hIHBvciBzZW1hbmEuDQoNCkEgY29udGludWFjacOzbiBzZSBtdWVzdHJhbiBhbGd1bm9zIGRlIGxvcyB2YWxvcmVzIG9ic2VydmFkb3MgZGUgbGEgYWNjaWRlbnRhbGlkYWQgc2VtYW5hbCBwb3IgY2xhc2UgeSBwb3IgY29tdW5hLCBjb24gc3UgcmVzcGVjdGl2byB2YWxvciBwcmVkaWNobyAow7psdGltYSBjb2x1bW5hKSBlbiBlbCBjb25qdW50byBkZSBwcnVlYmEuDQoNCmBgYHtyLCByb3dzLnByaW50ID0gNSwgZWNobyA9IEZ9DQpkYXRhLmZyYW1lKHRlc3Rfc2VtYW5hX2NvbXVuYSwgQS5QUkVESUNIQSA9IHlfZXN0X3Rlc3Rfc2VtYW5hX2NvbXVuYTMpDQpgYGANCg0KYGBge3IsIGluY2x1ZGU9Rn0NCnJlbW92ZSh5X2VzdF90cmFpbl9zZW1hbmFfY29tdW5hMCwgeV9lc3RfdHJhaW5fc2VtYW5hX2NvbXVuYTEsIHlfZXN0X3RyYWluX3NlbWFuYV9jb211bmEyLCB5X2VzdF90cmFpbl9zZW1hbmFfY29tdW5hMywgeV9lc3RfdGVzdF9zZW1hbmFfY29tdW5hMCwgeV9lc3RfdGVzdF9zZW1hbmFfY29tdW5hMSwgeV9lc3RfdGVzdF9zZW1hbmFfY29tdW5hMiwgeV9lc3RfdGVzdF9zZW1hbmFfY29tdW5hMyxtc2VfdHJhaW5fc2VtYW5hX2NvbXVuYTAsbXNlX3RyYWluX3NlbWFuYV9jb211bmExLG1zZV90cmFpbl9zZW1hbmFfY29tdW5hMixtc2VfdHJhaW5fc2VtYW5hX2NvbXVuYTMsbXNlX3Rlc3Rfc2VtYW5hX2NvbXVuYTAsbXNlX3Rlc3Rfc2VtYW5hX2NvbXVuYTEsbXNlX3Rlc3Rfc2VtYW5hX2NvbXVuYTIsbXNlX3Rlc3Rfc2VtYW5hX2NvbXVuYTMpDQpgYGANCg0KIyMjIE1vZGVsb3MgLSB1bmlkYWQgZGUgdGllbXBvIE1lcw0KDQpQYXJhIGVzdGUgbW9kZWxvIHNlIGFncnVwYSBsYSB1bmlkYWQgZGUgdGllbXBvIHBvciBtZXNlcywgZW4gbG9zIGN1YWxlcyBzZSBzdW1hIGxhIGNhbnRpZGFkIGRlIGFjY2lkZW50ZXMgcXVlIHN1Y2VkZW4gZWwgZWwgbWlzbW8sIHRhbWJpw6luIHNlIHN1bWEgZWwgbsO6bWVybyBkZSBkw61hcyBmZXN0aXZvcyBxdWUgaGF5IGVuIGRpY2hvIG1lcy4gTGEgYmFzZSBsdWNlIGNvbW8gc2lndWU6DQoNCmBgYHtyfQ0KYWNjaWRlbnRlc19tZXNfY29tdW5hIDwtIGFjY2lkZW50ZXNfZGlhX2NvbXVuYSAlPiUgZ3JvdXBfYnkoQ09NVU5BLCBDTEFTRSwgUEVSSU9ETywgTUVTLCBUSUVNUE9fTUVTKSAlPiUNCiAgc3VtbWFyaXNlKERJQV9GRVNUSVZPID0gc3VtKERJQV9GRVNUSVZPKSwgQUNDSURFTlRBTElEQUQgPSBzdW0oQUNDSURFTlRBTElEQUQpKQ0KDQphY2NpZGVudGVzX21lc19jb211bmENCmBgYA0KDQpTZSByZWFsaXphIGxhIHBhcnRpY2nDs24gZGUgbGEgYmFzZSBkZSBlbnRyZW5hbWllbnRvIHkgZGUgcHJ1ZWJhDQoNCmBgYHtyfQ0KdGVzdF9tZXNfY29tdW5hIDwtIGFjY2lkZW50ZXNfbWVzX2NvbXVuYVthY2NpZGVudGVzX21lc19jb211bmEkUEVSSU9ETyA9PSAyMDE4LCBdDQp0cmFpbl9tZXNfY29tdW5hIDwtIGFjY2lkZW50ZXNfbWVzX2NvbXVuYVthY2NpZGVudGVzX21lc19jb211bmEkUEVSSU9ETyAlaW4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoMjAxNCwgMjAxNSwgMjAxNiwgMjAxNyksIF0NCmBgYA0KDQpBaG9yYSBzZSBhanVzdGFuIGxvcyBtb2RlbG9zIG1peHRvcyBjb24gZWZlY3RvcyBhbGVhdG9yaW9zIGVuIENPTVVOQSB5IENMQVNFIGFsIGlndWFsIHF1ZSBzZSBoaXpvIGEgbml2ZWwgZGlhcmlvIHkgc2VtYW5hbCwgZXMgZGVjaXIsIENMQVNFIGRlbnRybyBkZSBDT01VTkEuDQoNCmBgYHtyLCBpbmNsdWRlID0gRn0NCmxvYWQoZmlsZSA9ICJtb2RfbWVzX2NvbXVuYTAuUkRhdGEiKQ0KbG9hZChmaWxlID0gIm1vZF9tZXNfY29tdW5hMS5SRGF0YSIpDQpsb2FkKGZpbGUgPSAibW9kX21lc19jb211bmEyLlJEYXRhIikNCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm1vZF9tZXNfY29tdW5hMCA8LSBnbG1lcihBQ0NJREVOVEFMSURBRCB+ICAxICsgKDEgfCBDT01VTkEvQ0xBU0UpLA0KICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX21lc19jb211bmEsIGZhbWlseT0gcG9pc3NvbigpKQ0KbW9kX21lc19jb211bmExIDwtIGdsbWVyKEFDQ0lERU5UQUxJREFEIH4gIERJQV9GRVNUSVZPICsgKDEgfCBDT01VTkEvQ0xBU0UpLA0KICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluX21lc19jb211bmEsIGZhbWlseT0gcG9pc3NvbigpKQ0KbW9kX21lc19jb211bmEyIDwtIGdsbWVyKEFDQ0lERU5UQUxJREFEIH4gIERJQV9GRVNUSVZPICsgKDEgKyBESUFfRkVTVElWTyB8IENPTVVOQS9DTEFTRSksDQogICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fbWVzX2NvbXVuYSwgZmFtaWx5PSBwb2lzc29uKCkpDQpgYGANCg0KQW5hbMOtc2lzIGRlIHZhcmlhbnphIEFOT1ZBDQoNCmBgYHtyfQ0KYW5vdmEobW9kX21lc19jb211bmEwLCBtb2RfbWVzX2NvbXVuYTEsIG1vZF9tZXNfY29tdW5hMikNCmBgYA0KDQpTZSBvYnNlcnZhIHF1ZSBlbCBtb2RlbG8gKm1vZF9tZXNfY29tdW5hMiosIGVsIGN1YWwgdGllbmUgaW50ZXJjZXB0byB5IHBlbmRpZW50ZSBhbGVhdG9yaWEgY29uIGxhIGNhcmFjdGVyw61zdGljYSBESUFfRkVTVElWTyBubyBhcG9ydGEgc2lnbmlmaWNhdGl2YW1lbnRlIGEgbGEgZXhwbGljYWNpw7NuIGRlIGxhIGFjY2lkZW50YWxpZGFkIHNlZ8O6biBlbCBhbsOhbGlzaXMgZGUgdmFyaWFuemEgQU5PVkEuIFNpbiBlbWJhcmdvLCBzZSBwcm9jZWRlIGEgY2FsY3VsYXIgbG9zIE1TRSBkZSBlbnRyZW5hbWllbnRvIHkgZGUgcHJ1ZWJhLg0KDQpgYGB7cn0NCiMgTW9kZWxvIDANCnlfZXN0X3RyYWluX21lc19jb211bmEwIDwtIHJvdW5kKHByZWRpY3QobW9kX21lc19jb211bmEwLCBuZXdkYXRhID0gdHJhaW5fbWVzX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQp5X2VzdF90ZXN0X21lc19jb211bmEwIDwtIHJvdW5kKHByZWRpY3QobW9kX21lc19jb211bmEwLCBuZXdkYXRhID0gdGVzdF9tZXNfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KbXNlX3RyYWluX21lc19jb211bmEwIDwtIHJvdW5kKE1TRSh0cmFpbl9tZXNfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90cmFpbl9tZXNfY29tdW5hMCksNCkNCm1zZV90ZXN0X21lc19jb211bmEwIDwtIHJvdW5kKE1TRSh0ZXN0X21lc19jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90ZXN0X21lc19jb211bmEwKSw0KQ0KDQojIE1vZGVsbyAxDQp5X2VzdF90cmFpbl9tZXNfY29tdW5hMSA8LSByb3VuZChwcmVkaWN0KG1vZF9tZXNfY29tdW5hMSwgbmV3ZGF0YSA9IHRyYWluX21lc19jb211bmEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KeV9lc3RfdGVzdF9tZXNfY29tdW5hMSA8LSByb3VuZChwcmVkaWN0KG1vZF9tZXNfY29tdW5hMSwgbmV3ZGF0YSA9IHRlc3RfbWVzX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIiksMCkNCm1zZV90cmFpbl9tZXNfY29tdW5hMSA8LSByb3VuZChNU0UodHJhaW5fbWVzX2NvbXVuYSRBQ0NJREVOVEFMSURBRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9lc3RfdHJhaW5fbWVzX2NvbXVuYTEpLDQpDQptc2VfdGVzdF9tZXNfY29tdW5hMSA8LSByb3VuZChNU0UodGVzdF9tZXNfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlfZXN0X3Rlc3RfbWVzX2NvbXVuYTEpLDQpDQoNCiMgTW9kZWxvIDINCnlfZXN0X3RyYWluX21lc19jb211bmEyIDwtIHJvdW5kKHByZWRpY3QobW9kX21lc19jb211bmEyLCBuZXdkYXRhID0gdHJhaW5fbWVzX2NvbXVuYSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpLDApDQp5X2VzdF90ZXN0X21lc19jb211bmEyIDwtIHJvdW5kKHByZWRpY3QobW9kX21lc19jb211bmEyLCBuZXdkYXRhID0gdGVzdF9tZXNfY29tdW5hLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKSwwKQ0KbXNlX3RyYWluX21lc19jb211bmEyIDwtIHJvdW5kKE1TRSh0cmFpbl9tZXNfY29tdW5hJEFDQ0lERU5UQUxJREFELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2VzdF90cmFpbl9tZXNfY29tdW5hMiksNCkNCm1zZV90ZXN0X21lc19jb211bmEyIDwtIHJvdW5kKE1TRSh0ZXN0X21lc19jb211bmEkQUNDSURFTlRBTElEQUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9lc3RfdGVzdF9tZXNfY29tdW5hMiksNCkNCmBgYA0KDQoNCmBgYHtyLCBpbmNsdWRlPUZ9DQpkYXRvc19tc2UgPC0gZGF0YS5mcmFtZShNb2RlbG9fTWl4dG9fUG9pc3NvbiA9IGMoIkFDQ0lERU5UQUxJREFEIH4gMSArICgxIHwgQ09NVU5BL0NMQVNFKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNDSURFTlRBTElEQUQgfiBESUFfRkVTVElWTyArICgxIHwgQ09NVU5BL0NMQVNFKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQUNDSURFTlRBTElEQUQgfiBESUFfRkVTVElWTyArICgxICsgRElBX0ZFU1RJVk8gfCBDT01VTkEvQ0xBU0UpIiksDQogICAgICAgICAgICAgICAgICAgICAgICBUcmFpbl9NU0UgPSBjKG1zZV90cmFpbl9tZXNfY29tdW5hMCwgbXNlX3RyYWluX21lc19jb211bmExLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtc2VfdHJhaW5fbWVzX2NvbXVuYTIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgVGVzdF9NU0UgPSBjKG1zZV90ZXN0X21lc19jb211bmEwLCBtc2VfdGVzdF9tZXNfY29tdW5hMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtc2VfdGVzdF9tZXNfY29tdW5hMikpDQpkYXRvc19tc2UgPC0gZGF0b3NfbXNlICU+JQ0KICBtdXRhdGUoUG9yY2VudGFqZV9WYXJpYWNpb24gPSByb3VuZCgxMDAqYWJzKFRyYWluX01TRS1UZXN0X01TRSkvKFRyYWluX01TRStUZXN0X01TRSksIDIpKQ0KYGBgDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQprbml0cjo6a2FibGUoZGF0b3NfbXNlKQ0KYGBgDQoNCkF1bnF1ZSBlbCBtb2RlbG8gKm1vZF9tZXNfY29tdW5hMiogbm8gdGllbmUgdW5hIG1lam9yYSBtdXkgc2lnbmlmaWNhdGl2YSByZXNwZWN0byBhbCBtb2RlbG8gKm1vZF9tZXNfY29tdW5hMSosIHNpZ3VlIHNpZW5kbyBtZWpvciBjb24gZWwgY3LDrXRlcmlvIGRlbCAqVGVzdF9NU0UqLCBwb3IgbG8gcXVlIGVsIG1vZGVsbyBlbGVnaWRvIGVzIGVsIG1vZGVsbyAqKm1vZF9tZXNfY29tdW5hMioqDQoNCiQkXHdpZGVoYXR7XHRleHR7QUNDSURFTlRBTElEQUR9fSA9IFx0ZXh0e0RJQV9GRVNUSVZPfSArICgxICsgXHRleHR7RElBX0ZFU1RJVk99IHwgXHRleHR7Q09NVU5BL0NMQVNFfSkkJA0KQSBjb250aW51YWNpw7NuIHNlIGVuc2XDsWEgbGEgY29tcGFyYWNpw7NuIGVudHJlIGxhIGFjY2lkZW50YWxpZGFkIG9ic2VydmFkYSB5IGxhIGFjY2lkZW50YWxpZGFkIHByZWRpY2hhIG1lbnN1YWxtZW50ZSBwYXJhIGxhIGJhc2UgZGUgcHJ1ZWJhIChjb2x1bW5hIGZpbmFsKS4NCg0KYGBge3IsIHJvd3MucHJpbnQgPSA1fQ0KZGF0YS5mcmFtZSh0ZXN0X21lc19jb211bmEsIEEuUFJFRElDSEEgPSB5X2VzdF90ZXN0X21lc19jb211bmEyKQ0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZ9DQpyZW1vdmUoeV9lc3RfdHJhaW5fbWVzX2NvbXVuYTAseV9lc3RfdHJhaW5fbWVzX2NvbXVuYTEseV9lc3RfdHJhaW5fbWVzX2NvbXVuYTIseV9lc3RfdGVzdF9tZXNfY29tdW5hMCx5X2VzdF90ZXN0X21lc19jb211bmExLHlfZXN0X3Rlc3RfbWVzX2NvbXVuYTIsbXNlX3RyYWluX21lc19jb211bmEwLG1zZV90cmFpbl9tZXNfY29tdW5hMSxtc2VfdHJhaW5fbWVzX2NvbXVuYTIsbXNlX3Rlc3RfbWVzX2NvbXVuYTAsbXNlX3Rlc3RfbWVzX2NvbXVuYTEsbXNlX3Rlc3RfbWVzX2NvbXVuYTIpDQpgYGANCg0KIyMgTW9kZWxvcyBwcmVkaWN0aXZvcyBwYXJhIGxvcyBCYXJyaW9zDQoNCkxhIGJhc2UgZGUgZGF0b3MgcGFyYSByZWFsaXphciBlbCBhanVzdGUgZGUgbG9zIGJhcnJpb3Mgc2Ugb2J0dXZvIGlndWFsIGEgY29tbyBzZSByZWFsaXrDsyBwYXJhIGxhcyBjb211bmFzLCBlcyBkZWNpciwgY29uIHVuIGBleHBhbmQuZ3JpZGAgcGVybyBlc3RhIHZleiBhZ3J1cGFuZG8gbGEgYmFzZSBkZSBkYXRvcyBwb3IgYmFycmlvcy4NCg0KUmVhbGl6YW5kbyBlbCBtaXNtbyBtw6l0b2RvIHZpc3RvIGFudGVyaW9ybWVudGUgcGFyYSBsYSBzZWxlY2Npw7NuIGRlIG1vZGVsb3MgZW4gbGFzIGNvbXVuYXMsIHNlIGVuY3VlbnRyYW4gbG9zIHNpZ3VpZW50ZSBtb2RlbG9zIHBhcmEgbG9zIGJhcnJpb3MuDQoNCiMjIyBNb2RlbG8gLSB1bmlkYWQgZGUgdGllbXBvIETDrWENCg0KYGBge3IsIGluY2x1ZGU9Rn0NCmxvYWQoZmlsZSA9ICJhY2NpZGVudGVzX2RpYV9iYXJyaW8uUkRhdGEiKQ0KYGBgDQoNCkJhc2UgZGUgZGF0b3MgYWdydXBhZGEgcGFyYSBsYSBhY2NpZGVudGFsaWRhZCAobsO6bWVybyBkZSBhY2NpZGVudGVzKSBkaWFyaWEuDQoNCmBgYHtyLCByb3dzLnByaW50ID0gNX0NCmFjY2lkZW50ZXNfZGlhX2JhcnJpbyAjIEJhc2UgZGUgZGF0b3MgZGlhcmlhDQpgYGANCg0KDQpTZSBlbmNvbnRyw7MgcXVlIGVsIG1vZGVsbyBxdWUgbWVqb3IgZXhwbGljYSBlc3RhIGFjY2lkZW50YWxpZGFkIGVzOg0KDQoNCg0K